Make a clean environment

 rm(list=ls())


Load packages

packages.list <- c("ggplot2","treeio","ggtree","ggnewscale","ape","dplyr","tidyverse","tidyr","phytools","RColorBrewer","lubridate","readxl","ggforce","ggstance","ggridges","cowplot","hexbin","scales","haven","network","ggnetwork","intergraph","igraph","ggraph","graphlayouts","scatterpie","maps","mapdata","maptools","rgdal","rgeos","broom","ggrepel","ggridges","magick","ggbeeswarm","ggrastr", "extrafont")

#"plyr","Cairo","ggmap","emojifont","rPinecone","pairsnp","CoordinateCleaner","gridExtra","dendextend","ggdendro",

#BiocManager::install("ggtree")
#BiocManager::install("treeio")

for(pkg in packages.list){
  eval(bquote(library(.(pkg)))) }
Registering fonts with R


Confirm current environmental setup

R.Version()
$platform
[1] "x86_64-apple-darwin17.0"

$arch
[1] "x86_64"

$os
[1] "darwin17.0"

$system
[1] "x86_64, darwin17.0"

$status
[1] ""

$major
[1] "4"

$minor
[1] "1.2"

$year
[1] "2021"

$month
[1] "11"

$day
[1] "01"

$`svn rev`
[1] "81115"

$language
[1] "R"

$version.string
[1] "R version 4.1.2 (2021-11-01)"

$nickname
[1] "Bird Hippie"
print(sessionInfo())
R version 4.1.2 (2021-11-01)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Monterey 12.6.1

Matrix products: default
LAPACK: /Library/Frameworks/R.framework/Versions/4.1/Resources/lib/libRlapack.dylib

locale:
[1] en_GB.UTF-8/en_GB.UTF-8/en_GB.UTF-8/C/en_GB.UTF-8/en_GB.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] extrafont_0.19     ggrastr_1.0.1      ggbeeswarm_0.6.0   magick_2.7.3      
 [5] ggrepel_0.9.1      broom_1.0.0        rgeos_0.5-9        rgdal_1.5-30      
 [9] maptools_1.1-4     sp_1.4-6           mapdata_2.3.0      scatterpie_0.1.7  
[13] graphlayouts_0.7.2 ggraph_2.0.5       igraph_1.3.5       intergraph_2.0-2  
[17] ggnetwork_0.5.10   network_1.17.1     haven_2.4.3        scales_1.2.1      
[21] hexbin_1.28.2      cowplot_1.1.1      ggridges_0.5.3     ggstance_0.3.5    
[25] ggforce_0.3.3      readxl_1.3.1       lubridate_1.8.0    RColorBrewer_1.1-3
[29] phytools_0.7-90    maps_3.4.0         forcats_0.5.1      stringr_1.4.1     
[33] purrr_1.0.1        readr_2.1.3        tidyr_1.1.4        tibble_3.1.8      
[37] tidyverse_1.3.2    dplyr_1.0.7        ape_5.6-2          ggnewscale_0.4.5  
[41] ggtree_3.2.1       treeio_1.18.1      ggplot2_3.3.6      vcfR_1.13.0       

loaded via a namespace (and not attached):
 [1] backports_1.4.0         fastmatch_1.1-3         plyr_1.8.6             
 [4] lazyeval_0.2.2          splines_4.1.2           digest_0.6.30          
 [7] yulab.utils_0.0.4       htmltools_0.5.2         viridis_0.6.2          
[10] fansi_1.0.3             magrittr_2.0.3          googlesheets4_1.1.0    
[13] cluster_2.1.2           tzdb_0.3.0              ggfittext_0.9.1        
[16] modelr_0.1.8            extrafontdb_1.0         colorspace_2.0-3       
[19] rvest_1.0.2             xfun_0.34               crayon_1.5.2           
[22] jsonlite_1.8.3          phangorn_2.8.0          glue_1.6.2             
[25] polyclip_1.10-0         gtable_0.3.1            gargle_1.4.0           
[28] Rttf2pt1_1.3.10         DBI_1.1.1               Rcpp_1.0.9             
[31] plotrix_3.8-2           viridisLite_0.4.1       tmvnsim_1.0-2          
[34] gridGraphics_0.5-1      tidytree_0.3.6          foreign_0.8-81         
[37] httr_1.4.5              ellipsis_0.3.2          pkgconfig_2.0.3        
[40] farver_2.1.1            dbplyr_2.1.1            utf8_1.2.2             
[43] ggplotify_0.1.0         tidyselect_1.2.0        rlang_1.1.0            
[46] munsell_0.5.0           cellranger_1.1.0        tools_4.1.2            
[49] cli_3.4.1               generics_0.1.1          statnet.common_4.5.0   
[52] evaluate_0.17           fastmap_1.1.0           yaml_2.3.6             
[55] knitr_1.40              fs_1.5.2                tidygraph_1.2.0        
[58] nlme_3.1-153            aplot_0.1.1             xml2_1.3.3             
[61] compiler_4.1.2          rstudioapi_0.13         beeswarm_0.4.0         
[64] reprex_2.0.1            clusterGeneration_1.3.7 tweenr_1.0.2           
[67] stringi_1.7.8           lattice_0.20-45         Matrix_1.3-4           
[70] vegan_2.6-2             permute_0.9-7           vctrs_0.5.0            
[73] pillar_1.8.1            lifecycle_1.0.3         combinat_0.0-8         
[76] patchwork_1.1.1         R6_2.5.1                gridExtra_2.3          
[79] treemapify_2.5.5        vipor_0.4.5             codetools_0.2-18       
[82] MASS_7.3-54             assertthat_0.2.1        withr_2.5.0            
[85] pinfsc50_1.2.0          mnormt_2.0.2            mgcv_1.8-38            
[88] expm_0.999-6            parallel_4.1.2          hms_1.1.2              
[91] quadprog_1.5-8          grid_4.1.2              ggfun_0.0.4            
[94] coda_0.19-4             rmarkdown_2.17          googledrive_2.1.0      
[97] numDeriv_2016.8-1.1     scatterplot3d_0.3-41   


Make some shortcuts for plotting

y.theme.strip <- theme(axis.title.y = element_blank(), axis.text.y = element_blank(), axis.ticks.y= element_blank())
y.theme.strip.partial <- theme(axis.text.y = element_blank(), axis.ticks.y= element_blank())

x.theme.strip <- theme(axis.title.x = element_blank(), axis.text.x = element_blank(), axis.ticks.x= element_blank())
x.theme.strip.partial <- theme(axis.text.x = element_blank(), axis.ticks.x= element_blank())
x.theme.strip.labs <- theme(axis.text.x = element_blank(),axis.title.x = element_blank())

x.theme.axis.rotate <- theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))

legend.strip <- theme(legend.position = "none")

theme.text.size <- theme(text = element_text(size = 10))

'%notin%' <- Negate('%in%')

max.font.size <- 7
basic.font.size <- 6
min.font.size <- 5.25
theme.text.size <- theme(text = element_text(size = basic.font.size))
theme.text.size.within <- (5/14)*min.font.size
panel.lab.size <- 10


Specify raw data - global dataset

#Data_input_directory <- "/Users/mb29/Papers/Treponema_UK-PHE-gen-epi_2021/Data/"
#Data_input_directory <- "/Users/mb29/Papers/Treponema_UK-PHE-gen-epi_2021/Rnotebook/Rnotebook_09-2022/data/"
Data_input_directory <- paste0(getwd(), "/inputdata/")


################################
#### Tree data 

# ML tree (refined dataset)
TPA.MLtree.file <- paste0(Data_input_directory,"TPA-uber.remasked.2020-11-10.goodcov25.gubbins.SNPs.aln.renamed.fix-zero-dist.treefile")

# Pyjar tree (refined dataset)
TPA.pyjar.file <- paste0(Data_input_directory,"TPA-uber.remasked.2020-11-10.goodcov25.gubbins.SNPs.aln.renamed.pyjar.tre")

# Full size BEAST2 analysis - previously generated as part of Beale, 2021.
full.beast2.tree.file <- paste0(Data_input_directory,"TPA-uber_beast2_strict-skyline-500M_10pop_consensus.tree")

# Ancestral reconstruction of global TPA ML tree from TreeTime (refined dataset)
TPA.treetime.ancestral.tree.file <- paste0(Data_input_directory,"TPA.annotated_tree.fix-hung.nexus")
TPA.treetime.ancestral.vcf.file <- paste0(Data_input_directory,"TPA-uber.midpoint.ancestral_sequences.fix-hung.vcf")
# Functionally annotated variants, extracted from snpEff vcf into tsv using snpSift
TPA.snpEff.file <- paste0(Data_input_directory,"TPA-uber.midpoint.ancestral_sequences.relab.bcf.ann.vcf.vartab.sepline.tsv")
# Gff file for SS14 reference genome, containing gene positions/annotations
SS14.gff.file <- paste0(Data_input_directory,"Treponema_pallidum_subs._pallidum_SS14.NC_021508.1.2021-06-13.gff")

################################
#### Meta data 

# Supplement from TPA-Uber paper - Beale, 2021 
TPA.meta2.file <- paste0(Data_input_directory,"Sup_Data1_Global_Sample-Metadata__09-2022.xlsx")

# England specific metadata collated by PHE/UKHSA
PHE.metadata.linked.file <- paste0(Data_input_directory,"Sup_Data2_TPA.UK-only.PHE.metadata.2022-02-02.xlsx")

# England specific mapping shapefile data with Public Health Boundaries
# Imported datafile from https://geoportal.statistics.gov.uk/datasets/public-health-england-centres-december-2016-full-clipped-boundaries-in-england/explore?location=52.950000%2C-2.000000%2C6.88
UK.publichealth.shapefile.data <- paste0(Data_input_directory,"Public_Health_England_Centres_(December_2016)_Boundaries")


################################
#### Externally plotted figures (e.g. GrapeTree) for inclusion in multipanel figures

# Externally plotted grapetree minimum spanning tree for whole of England - code to extract subtree that was used to make this is included later in this Rnotebook
TPA.UK.Grapetree.sublineages.file <- paste0(Data_input_directory,"TPA-UK-2022-02-03.sublineage-MSTree.Inkscaped.svg")

# Externally plotted grapetree minimum spanning tree for whole of England - 3-variable plots
TPA.UK.Grapetree.3way.file <- paste0(Data_input_directory,"TPA-UK-2022-02-16.-MSTree_3-way-figure.Inscaped-3.svg")

# Externally plotted grapetree minimum spanning tree for whole of England - HIV status
TPA.UK.Grapetree.HIV.file <- paste0(Data_input_directory,"TPA-UK-2022-02-03.HIVstatus-MSTree_inkscaped.svg")

# Externally plotted grapetree minimum spanning tree for North East England networks
TPA.NorthEastEngland.Grapetree.file <- paste0(Data_input_directory,"TPA-UK-NorthEast-2022-02-26.GenderOrientation-MSTree.inkscaped.+node-counts+GBMSM.svg")


Specify directory to output plots

Figure_output_directory <- paste0(getwd(), "/Figures_revision_03-2023/")

#"/Users/mb29/Papers/Treponema_UK-PHE-gen-epi_2021/Figures/Figure_Drafting/Working_Figures_08-2022/"


Read in trees

TPA.MLtree <- midpoint.root(read.tree(TPA.MLtree.file))
TPA.pyjar.tree <- midpoint.root(read.tree(TPA.pyjar.file))


Read in final output metadata from Global Uber study (Beale 2021)

TPA.meta2.1 <- readxl::read_excel(TPA.meta2.file,sheet="Supplementary_Data1_Sample-Meta")


Create a colour scheme for Lineages, Countries and Continents (consistent with Beale, 2021)

# Colouring for country
continental.country.cols.brew2 <- unique(TPA.meta2.1[,c("Geo_Country","Continent")])
continental.country.cols.brew2 <- continental.country.cols.brew2[order(continental.country.cols.brew2$Continent,continental.country.cols.brew2$Geo_Country),]

continental.country.cols.brew2$country.col <- c("#ec7014","#fec44f","#de2d26","#fb6a4a","#bdbdbd","#737373",brewer.pal(n=8,"Purples")[4:8],brewer.pal(n=8,"Blues")[3:8],brewer.pal(n=5,"Greens")[3:5],"#c51b8a","#8c510a")

# Colouring for Continent
continental.cols.brew2 <- data.frame(Continent=sort(unique(TPA.meta2.1$Continent)),stringsAsFactors=F)
continental.cols.brew2$continent.col <- c("#fec44f","#de2d26","#bdbdbd","#2171b5","#74c476","#c51b8a","#ec7014")


# Colouring for TPA Lineage
TPA_Lineage.cols <- data.frame(Lineage=sort(unique(TPA.meta2.1$TPA_Lineage)),stringsAsFactors=F)
TPA_Lineage.cols$Lineage.col <- c("royalblue2", "indianred1")
#c("#436eee", "#666666","#ff6a6a")
TPA_Lineage.cols$Lineage <- factor(TPA_Lineage.cols$Lineage, levels=c("Nichols","SS14","outlier"))

# Lineage Hexcodes
# royalblue2 #436eee
# indianred1 #ff6a6a


Define colours for sublineages

# Define sublineage clustering scheme using brew colourscales
sublineages.cols.brew <- data.frame(unique(TPA.meta2.1[,c("TPA_Lineage","TPA.pinecone.sublineage")]), stringsAsFactors = F)
sublineages.cols.brew <- sublineages.cols.brew[order(sublineages.cols.brew$TPA_Lineage,sublineages.cols.brew$TPA.pinecone.sublineage),]

sublineages.cols.brew$sublin.order <- as.numeric(as.character(sublineages.cols.brew$TPA.pinecone.sublineage))
Warning: NAs introduced by coercion
sublineages.cols.brew <- sublineages.cols.brew[order(sublineages.cols.brew$sublin.order),]

# For revised bootstrapped clusters
sublineages.cols.brew$sublineage.cols <- c("#FC9272","#EF3B2C",brewer.pal(n=4,"Greens")[2:4],brewer.pal(n=4,"YlOrBr")[c(2,3)],brewer.pal(n=6,"Blues")[2:6],brewer.pal(n=6,"Purples")[2:6],"grey80","grey80","grey80","grey80")
  
sublineages.cols.brew <- unique(sublineages.cols.brew[,c("TPA.pinecone.sublineage","sublineage.cols")])
sublineages.cols.brew <- sublineages.cols.brew[order(as.numeric(as.character(sublineages.cols.brew$TPA.pinecone.sublineage))),]
Warning in order(as.numeric(as.character(sublineages.cols.brew$TPA.pinecone.sublineage))) :
  NAs introduced by coercion
sublineages.cols.brew$TPA.pinecone.sublineage <- factor(sublineages.cols.brew$TPA.pinecone.sublineage, levels=sublineages.cols.brew$TPA.pinecone.sublineage)
sublineages.cols.brew <- sublineages.cols.brew[!is.na(sublineages.cols.brew$sublineage),]

colnames(sublineages.cols.brew) <- c("sublineage","sublineage.cols")
sublineages.cols.brew <- unique(sublineages.cols.brew)


Restrict analysis to high quality genomes (and tree)

TPA.meta2.1 <- TPA.meta2.1[TPA.meta2.1$finescale.analysis=="Yes",]


Create a “UK” variable, and a “PHE” variable

TPA.meta2.1$is.UK <- ifelse(TPA.meta2.1$Geo_Country=="UK","UK","Other")
TPA.meta2.1$is.PHE <- ifelse(TPA.meta2.1$Geo_Country=="UK" & grepl("PHE",TPA.meta2.1$Sample_Name),"PHE","Other")


# Prepare ML tree
TPA.MLtree.ggtree <- ggtree(TPA.MLtree,layout = "fan",open.angle = 10, right=T)
Scale for 'y' is already present. Adding another scale for 'y', which will
replace the existing scale.
# Prepare country dataset
TPA.rawseq.countries.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Country=TPA.meta2.1$Geo_Country, stringsAsFactors = F)

# Prepare continent dataset
TPA.rawseq.continents.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Continent=TPA.meta2.1$Continent, stringsAsFactors = F)

# Prepare UK data strip
TPA.rawseq.UK.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, England=TPA.meta2.1$is.UK, stringsAsFactors = F)
TPA.rawseq.UK.p[TPA.rawseq.UK.p$England=="UK",] <- "England"

# Prepare PHE data strip
TPA.rawseq.PHE.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, PHE=TPA.meta2.1$is.PHE, stringsAsFactors = F)

# Prepare Major lineage dataset
TPA.rawseq.Lineage.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Lineage=TPA.meta2.1$TPA_Lineage, stringsAsFactors = F)

# Prepare sublineage lineage dataset
TPA.rawseq.subLineage.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Sublineage=TPA.meta2.1$TPA.pinecone.sublineage, stringsAsFactors = F)


# Prepare Year dataset (all samples)
TPA.rawseq.all.Years.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Year=TPA.meta2.1$Sample_Year, stringsAsFactors = F)


floor_5years  <- function(value){ return(value - value %% 5) }
TPA.meta2.1$Sample_5year.window <- paste0(floor_5years(as.numeric(TPA.meta2.1$Sample_Year)),"-",floor_5years(as.numeric(TPA.meta2.1$Sample_Year))+5)
Warning in floor_5years(as.numeric(TPA.meta2.1$Sample_Year)) :
  NAs introduced by coercion
Warning in floor_5years(as.numeric(TPA.meta2.1$Sample_Year)) :
  NAs introduced by coercion
# Some samples have uncertain dates (up to 20-30 years uncertainty), but for the purposes of these plotting categories we'll use the centrepoint year
TPA.meta2.1$Sample_5year.window <- sapply(1:nrow(TPA.meta2.1), function(x) ifelse(TPA.meta2.1$Sample_Year[x]=="-",NA, ifelse(is.na(TPA.meta2.1$Sample_5year.window[x]),NA, ifelse(TPA.meta2.1$Sample_Year[x]=="1950-1980","1965-1970",ifelse(TPA.meta2.1$Sample_Year[x]=="1960-1980","1965-1970" ,ifelse(TPA.meta2.1$Sample_Year[x]=="1980-1999","1985-1990",TPA.meta2.1$Sample_5year.window[x]))))))


TPA.meta2.1$Sample_year.1990.cuttoff <- ifelse(TPA.meta2.1$Sample_Year>1990,TPA.meta2.1$Sample_Year,"<1990")

TPA.meta2.1$Sample_year.1999.cuttoff <- ifelse(TPA.meta2.1$Sample_Year>1999,TPA.meta2.1$Sample_Year,"<1999")
TPA.rawseq.year.cuttoff.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Sample.Year=TPA.meta2.1$Sample_year.1999.cuttoff, stringsAsFactors = F)



# Bring in PHE metadata

PHE.metadata.linked <- readxl::read_excel(PHE.metadata.linked.file)


Do some cleanup and factoring of variables


PHE.metadata.linked$age_group <- factor(PHE.metadata.linked$age_group, levels=rev(c("16-24","25-34","35-44","45+","Unknown")))

PHE.metadata.linked$london <- factor(PHE.metadata.linked$london,levels=rev(c("Yes","No","Unknown")))
PHE.metadata.linked$ukborn <- factor(PHE.metadata.linked$ukborn,levels=rev(c("Yes","No","Unknown")))
PHE.metadata.linked$hivpos <- factor(PHE.metadata.linked$hivpos, levels=rev(c("Yes","No","Unknown")))

# need to update terminology of 'MSM' to 'GBMSM'
PHE.metadata.linked[PHE.metadata.linked$gender_orientation=="MSM","gender_orientation"] <- "GBMSM"
PHE.metadata.linked$gender_orientation <- factor(PHE.metadata.linked$gender_orientation, levels=rev(c("MSW","GBMSM","WSM","MUnknown","Unknown")))

PHE.metadata.linked$phe_centre <- factor(PHE.metadata.linked$phe_centre, levels=rev(c("East Midlands", "East of England", "London", "North East", "North West", "South East", "South West", "West Midlands", "Yorkshire and Humber", "UK (not England)", "Not Known")))

PHE.metadata.linked$TPA.pinecone.sublineage <-  factor(PHE.metadata.linked$TPA.pinecone.sublineage, levels=sublineages.cols.brew$sublineage)



### Extract information about duplicates

PHE.metadata.duplicates <- PHE.metadata.linked[!is.na(PHE.metadata.linked$dup_flag),]
PHE.metadata.duplicates <- PHE.metadata.duplicates[!is.na(PHE.metadata.duplicates$Sample_Name),]


PHE.patient.matches <- data.frame(
    stringsAsFactors = FALSE,
                                   dup_flag = c("1A","1B",
                                                "2A","2B","3A","3B","4A",
                                                "4B","5A","5B"),
                                dup_Patient = c("Patient 1",
                                                "Patient 1","Patient 2",
                                                "Patient 2","Patient 3","Patient 3",
                                                "Patient 4","Patient 4",
                                                "Patient 5","Patient 5"),
                         dup_Patient_Sample = c("sample 1",
                                                "sample 2","sample 1",
                                                "sample 2","sample 1","sample 2",
                                                "sample 1","sample 2","sample 1",
                                                "sample 2")
                       )
                       

PHE.metadata.duplicates <- left_join(PHE.metadata.duplicates, PHE.patient.matches, by="dup_flag")

PHE.metadata.duplicates

Duplicate Samples missing metadata are all ‘new duplicates’ and were excluded due to low mapping coverage (all checked).
Samples labelled ‘ZA’ and ‘XB’ had duplicates in the original dataset, but the reciprocal pairs were excluded due to quality isues.
Available pairs - Patient 3, Patient 4

PHE.metadata.duplicates.paired <- PHE.metadata.duplicates[PHE.metadata.duplicates$dup_Patient %in% c("Patient 3","Patient 4"),]
PHE.metadata.duplicates.paired[order(PHE.metadata.duplicates.paired$dup_Patient, PHE.metadata.duplicates.paired$year,PHE.metadata.duplicates.paired$month),c("Sample_Name","dup_Patient", "month.fix", "year")]


These will be revisited later in the analysis.
Patient 4 HIV-ve MSM (45+), UK born, PHE region D 2 samples, collected in the same month and year Both samples are sublineage 1, and identical (0 pwSNPs) Likely the same infection (depending on dates, treatment, etc), but can’t rule out reinfection with same strain.
Patient 3 HIV-ve MSM (35-44), not UK born, based in London (C) 2 samples, collected 9 months apart Both samples are sublineage 1, but have 7 pairwise SNPs between them (loads!) Reinfection – probably from a different transmission network

However, based on the sample dates, as well as the outcome of the downstream genetic analysis, we can see that Patient 3 has duplicate infection events (different dates, 10 months apart) and the genomes are distinct (7 SNPs apart), whereas Patient 4 samples were collected in the same month and year (i.e. are likely duplicates from the same infection) and has identical genomes.
For downstream analysis purposes, we will retain both samples for Patient 3 (discrete infections), but exclude one sample from Patient 4 (duplicate infection samples) - ‘PHE150126A’ has much better genome coverage, so exclude ‘PHE150125A’

### Further Exclusions
PHE130056A - duplicate of PHE130057B (already removed, so not relevant) - don’t exclude! PHE170402A - quality control sample PHE170378A - quality control sample


Exclude duplicate sequences

duplicate.exclusion.list <- c("PHE150125A","PHE170402A","PHE170378A")
PHE.metadata.linked <- PHE.metadata.linked[PHE.metadata.linked$Sample_Name %notin% duplicate.exclusion.list,]


Moving on…

Define some colour schemes

# define some colors for each region
PHE.region.cols.brew <- data.frame(UKHSA.region=c("North East", "North West", "Yorkshire and Humber", "East Midlands", "West Midlands", "East of England", "London", "South East","South West","UK (not England)", "Not Known"), stringsAsFactors=F)
PHE.region.cols.brew$region.col <- c("#A6CEE3","#1F78B4","#CAB2D6","#33A02C","#B2DF8A","#FF7F00","#E31A1C","#FB9A99","#D4BB02","grey75","grey25")

# HIV color scheme
PHE.hiv.cols <- data.frame(hivpos=rev(sort(unique(PHE.metadata.linked$hivpos))), stringsAsFactors=F)
PHE.hiv.cols$hiv.cols <- c("#1f78b4","#b2df8a","grey75")

# Orientation colour scheme
PHE.orientation.cols <- data.frame(orientation=rev(sort(unique(PHE.metadata.linked$gender_orientation))), stringsAsFactors=F)
PHE.orientation.cols$orientation <- factor(PHE.orientation.cols$orientation, levels=rev(sort(unique(PHE.metadata.linked$gender_orientation))), labels=c("MSW","GBMSM","WSM","MUnknown","Unknown"))
PHE.orientation.cols$orientation.cols <- c("#1f78b4","#b2df8a","#fb9a99","#a6cee3","grey75")

# UK born colour scheme
PHE.ukborn.cols <- data.frame(ukborn=rev(sort(unique(PHE.metadata.linked$ukborn))),ukborn.cols=c("#1f78b4","#b2df8a","grey75"),stringsAsFactors = F)

# London based colour scheme
PHE.london.cols <- data.frame(london=rev(sort(unique(PHE.metadata.linked$london))),london.cols=c("#1f78b4","#b2df8a","grey75"),stringsAsFactors = F)


# Age group colour scheme
PHE.Age.cols <- data.frame(age_group=rev(sort(unique(PHE.metadata.linked$age_group))),stringsAsFactors = T)
PHE.Age.cols$age_group.cols <- c(brewer.pal(n=4,"YlGnBu"),"grey75")

# Sample Date colour scheme
PHE.year.cols <- data.frame(year=(sort(unique(PHE.metadata.linked$year))),stringsAsFactors = T)
PHE.year.cols$year.cols <- brewer.pal(n=7,"YlOrRd")

# Sample Date (all global data, but with 1990 cuttoff)
TPA.year.cuttoff.cols <- data.frame(date.cuttoff=c("<1999",1999:2019), date.cuttoff.col=c("#F2F2F2",colorRampPalette(brewer.pal(7, "YlOrRd"))(length(1999:2019))))



##### ## First describe the sequenced population as a whole
Set order of PHE regions

PHE.metadata.linked$phe_centre <- factor(PHE.metadata.linked$phe_centre, levels=rev(PHE.region.cols.brew$UKHSA.region))


Generate some basic statistics about geographical PHE regions (anonymised)

PHE.count.all <- PHE.metadata.linked %>% 
  dplyr::summarise(count.per.region=n())

PHE.count.years <- PHE.metadata.linked %>% 
  dplyr::group_by(year) %>%
  dplyr::summarise(count.per.year=n()) %>%
  ungroup() %>%
  dplyr::mutate(perc.per.year=(count.per.year/sum(count.per.year))*100)

# Generate some stats about HIV status
PHE.HIV.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(hivpos) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::mutate(fraction=Count/total.region) %>%
  dplyr::arrange(desc(hivpos), .by_group=T) %>%
  dplyr::mutate(cum_fract = cumsum(fraction)) %>%
  dplyr::mutate(cum_fract.mid = cum_fract-(fraction/2)) %>%
  dplyr::mutate(HIV.perc=(Count/sum(Count)*100))
  
# Generate some stats about gender orientation
PHE.orientation.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(gender_orientation) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::arrange(desc(gender_orientation), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>% 
  dplyr::mutate(orientation.perc=(Count/sum(Count)*100))

# Generate some stats about UK born (vague category that's unfortunately only marginally helpful)
PHE.UKborn.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(ukborn) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::arrange(desc(ukborn), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>% 
  dplyr::mutate(UKborn.perc=(Count/sum(Count)*100))
  
# Generate some stats about London based
PHE.London.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(london) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::arrange(desc(london), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
  dplyr::mutate(London.perc=(Count/sum(Count)*100))

# Generate some stats about Age group
PHE.Age.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(age_group) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::arrange(desc(age_group), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
  dplyr::mutate(Age.perc=(Count/sum(Count)*100))

# Generate some stats about Lineage group
PHE.Lineage.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA_Lineage) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::arrange(desc(TPA_Lineage), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
  dplyr::mutate(Lineage.perc=(Count/sum(Count)*100))

# Generate some stats about sublineage group
PHE.sublineage.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA.pinecone.sublineage) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::arrange(desc(TPA.pinecone.sublineage), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
  dplyr::mutate(Sublineage.perc=(Count/sum(Count)*100))


Make some plots

# Make hbar plot of sample counts by region
p.all.hbarplot <- ggplot(PHE.count.all, aes(x=count.per.region,y="")) +
  geom_barh(stat="identity", position="stack", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
  scale_fill_manual(values="grey30") + 
  geom_text(data=PHE.count.all, aes((count.per.region+12), "",label=count.per.region), size=theme.text.size.within, inherit.aes = F) +
  labs(y="All", x="Sample Count") +
  coord_cartesian(xlim=c(0,260)) +
  guides(fill=guide_legend(nrow=4)) 
#p.all.hbarplot

# make temporal bubbleplot of counts by region
p.all.year.bubbleplot <- ggplot(PHE.count.years, aes(as.numeric(year), y="All")) +
  geom_point(alpha=0.65, aes(size=count.per.year)) + 
  geom_line(alpha=0.25) +
  guides(colour='none') +
  scale_size_area(max_size = 7,breaks=c(1,5,10,25,50)) + 
  guides(size=guide_legend(nrow=2)) +
  theme_light() +
  scale_fill_manual(values="grey30") + 
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
  labs(y="", x="Sample Year", size="Count") 
#p.all.year.bubbleplot

# Make proportional hbar plot of HIV status
p.all.hiv.hbarplot <- ggplot(PHE.HIV.counts, aes(Count,y="",fill=hivpos)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
  scale_fill_manual(name="HIV +ve",values=PHE.hiv.cols$hiv.cols, breaks=PHE.hiv.cols$hivpos) +
  labs(y="All", x="HIV +ve") +
  guides(fill=guide_legend(nrow=3)) +
  geom_text(data=PHE.HIV.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F) +
  NULL
#p.all.hiv.hbarplot

p.all.orientation.hbarplot <- ggplot(PHE.orientation.counts, aes(Count,y="",fill=gender_orientation)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
  scale_fill_manual(name="Orientation",values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) +
  labs(y="All", x="Orientation") +
  guides(fill=guide_legend(nrow=3)) +
  geom_text(data=PHE.orientation.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F)
#p.all.orientation.hbarplot

p.all.ukborn.hbarplot <- ggplot(PHE.UKborn.counts, aes(Count,y="",fill=ukborn)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
  scale_fill_manual(name="UK\nBorn",values=PHE.ukborn.cols$ukborn.cols, breaks=PHE.ukborn.cols$ukborn) +
  labs(y="All", x="UK Born") +
  #guides(fill=guide_legend(nrow=3)) +
  geom_text(data=PHE.UKborn.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F)
#p.all.ukborn.hbarplot

p.all.London.hbarplot <- ggplot(PHE.London.counts, aes(Count,y="",fill=london)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
  scale_fill_manual(name="London",values=PHE.london.cols$london.cols, breaks=PHE.london.cols$london) +
  labs(y="All", x="London") +
  guides(fill=guide_legend(nrow=3)) +
  geom_text(data=PHE.London.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F)
#p.all.London.hbarplot

p.all.Age.hbarplot <- ggplot(PHE.Age.counts, aes(Count,y="",fill=age_group)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
  scale_fill_manual(name="Age\nGroup",values=PHE.Age.cols$age_group.cols, breaks=PHE.Age.cols$age_group) +
  labs(y="All", x="Age Group") +
  guides(fill=guide_legend(nrow=3)) +
  geom_text(data=PHE.Age.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F)
#p.all.Age.hbarplot


Plot combined plot for ‘all samples’

PHE.all.combiplot.1 <- plot_grid(p.all.year.bubbleplot, p.all.hbarplot + y.theme.strip, p.all.orientation.hbarplot + y.theme.strip, p.all.hiv.hbarplot + y.theme.strip, p.all.Age.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(4,2,2,2,2), scale=0.9)

PHE.all.combiplot.1



Next just describe population distributions by PHE region

# generate some basic statistics about geographical PHE regions (anonymised)
PHE.geo.count <- PHE.metadata.linked %>% 
  dplyr::group_by(phe_centre) %>%
  dplyr::summarise(count.per.region=n()) %>%
  dplyr::mutate(total.count=sum(count.per.region),fraction=count.per.region/total.count)

PHE.geo.count.years <- PHE.metadata.linked %>% 
  dplyr::group_by(phe_centre,year) %>%
  dplyr::summarise(count.per.region.year=n())
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
PHE.geo.count.years.lineage <- PHE.metadata.linked %>% 
  dplyr::group_by(phe_centre,year,TPA_Lineage) %>%
  dplyr::summarise(count.per.region.year=n()) %>%
  dplyr::mutate(total.count.year=sum(count.per.region.year)) %>%
  dplyr::ungroup() %>%
  tidyr::pivot_wider(names_from=TPA_Lineage, values_from = count.per.region.year)
`summarise()` has grouped output by 'phe_centre', 'year'. You can override using the `.groups` argument.
PHE.geo.count.years.lineage[is.na(PHE.geo.count.years.lineage)] <- 0
PHE.geo.count.years.lineage$year <- as.numeric(PHE.geo.count.years.lineage$year)

# Generate some stats about HIV status
PHE.geo.HIV.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(phe_centre,hivpos) %>%
  dplyr::summarise(count.per.region.hiv=n()) %>%
  dplyr::mutate(total.region=sum(count.per.region.hiv)) %>%
  dplyr::mutate(fraction=count.per.region.hiv/total.region) %>%
  dplyr::arrange(desc(hivpos), .by_group=T) %>%
  dplyr::mutate(cum_fract = cumsum(fraction)) %>%
  dplyr::mutate(cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Double Check HIV status data for non-PHE dataset - confirmed no HIV+ves from non-MSM. 
PHE.sourcelab.HIV.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(is.PHE, gender_orientation, hivpos) %>%
  dplyr::summarise(count.per.orientation.hiv=n()) #%>%
`summarise()` has grouped output by 'is.PHE', 'gender_orientation'. You can override using the `.groups` argument.
  #dplyr::filter(is.PHE!="PHE")

# Get total population stats for HIV
PHE.all.HIV.counts <-  PHE.metadata.linked %>% 
  dplyr::group_by(hivpos) %>%
  dplyr::summarise(count.hiv=n()) %>%
  dplyr::mutate(count.total=sum(count.hiv), fraction=count.hiv/count.total)

  
# Generate some stats about gender orientation
PHE.orientation.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(gender_orientation) %>%
  dplyr::summarise(orientation.count=n()) %>%
  dplyr::mutate(orientation.percent=(orientation.count/sum(orientation.count)*100))

PHE.geo.orientation.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(phe_centre,gender_orientation) %>%
  dplyr::summarise(count.per.region.orientation=n()) %>%
  dplyr::mutate(total.region=sum(count.per.region.orientation)) %>%
  dplyr::arrange(desc(gender_orientation), .by_group=T) %>%
  dplyr::mutate(fraction=count.per.region.orientation/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>% 
  dplyr::mutate(orientation.percent=(count.per.region.orientation/sum(count.per.region.orientation)*100))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about UK born
PHE.geo.UKborn <- PHE.metadata.linked %>% 
  dplyr::group_by(phe_centre, ukborn) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::arrange(desc(ukborn), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
  
# Generate some stats about London based
PHE.geo.London <- PHE.metadata.linked %>% 
  dplyr::group_by(phe_centre, london) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::arrange(desc(london), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about Age group
PHE.geo.Age <- PHE.metadata.linked %>% 
  dplyr::group_by(phe_centre, age_group) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::arrange(desc(age_group), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about Lineage group
PHE.geo.Lineage <- PHE.metadata.linked %>% 
  dplyr::group_by(phe_centre, TPA_Lineage) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::arrange(desc(TPA_Lineage), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about sublineage group
PHE.geo.sublineage <- PHE.metadata.linked %>% 
  dplyr::group_by(phe_centre, TPA.pinecone.sublineage) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::arrange(desc(TPA.pinecone.sublineage), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.


Make some plots

# Make hbar plot of sample counts by region
p.region.hbarplot <- ggplot(PHE.geo.count, aes(count.per.region,phe_centre, fill=phe_centre)) +
  geom_barh(stat="identity", position="stack", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="UKHSA\nRegion",values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region) +
  geom_text(data=PHE.geo.count, aes((count.per.region+12), phe_centre,label=count.per.region), size=theme.text.size.within, inherit.aes = F) +
  labs(y="UKHSA Region", x="Sample Count") +
  #coord_cartesian(xlim=c(0,130)) +
  coord_cartesian(xlim=c(0,260)) +
  guides(fill=guide_legend(ncol=2)) 
#p.region.hbarplot

# make temporal bubbleplot of counts by region
p.region.year.bubbleplot <- ggplot(PHE.geo.count.years, aes(as.numeric(year), phe_centre, colour=phe_centre)) +
  geom_point(alpha=0.65, aes(size=count.per.region.year)) + 
  geom_line(alpha=0.25) +
  guides(colour='none') +
  scale_size_area(max_size = 7,breaks=c(1,5,10,25,50)) + 
  guides(size=guide_legend(nrow=2, direction = 'horizontal', byrow=T)) +
  theme_light() +
  scale_color_manual(name="UKHSA\nRegion",values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region) +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  labs(y="UKHSA Region", x="Sample Year", size="Count") 
#p.region.year.bubbleplot

# Or a barplot of lineage by year & PHE region?
p.region.year.bubbleplot.barplot.facet.lineage <- PHE.geo.count.years.lineage %>% tidyr::pivot_longer(c(SS14, Nichols), names_to="TPA_Lineage", values_to="Count") %>%
  ggplot(aes(year, Count, fill=TPA_Lineage)) + 
  geom_bar(stat='identity', width=0.6) + 
  facet_grid(phe_centre~., scales='free') +
  guides(size=guide_legend(nrow=2)) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="TPA\nLineage",values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage) +
  theme(strip.background = element_rect(color='white', fill='white',linetype="solid"), strip.text.y = element_text(color = "grey25", size=7, angle=0)) 
#p.region.year.bubbleplot.barplot.facet.lineage

# Make proportional hbar plot of HIV status
p.region.hiv.hbarplot <- ggplot(PHE.geo.HIV.counts, aes(count.per.region.hiv,phe_centre,fill=hivpos)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="HIV +ve",values=PHE.hiv.cols$hiv.cols, breaks=PHE.hiv.cols$hivpos) +
  labs(y="UKHSA Region", x="HIV +ve") +
  guides(fill=guide_legend(nrow=3)) +
  geom_text(data=PHE.geo.HIV.counts, aes(cum_fract.mid, phe_centre,label=count.per.region.hiv), size=theme.text.size.within, inherit.aes = F) +
  NULL
#p.region.hiv.hbarplot

p.region.orientation.hbarplot <- ggplot(PHE.geo.orientation.counts, aes(count.per.region.orientation,phe_centre,fill=gender_orientation)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="Orientation",values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) +
  labs(y="UKHSA Region", x="Orientation") +
  guides(fill=guide_legend(ncol=1)) +
  geom_text(data=PHE.geo.orientation.counts, aes(cum_fract.mid, phe_centre,label=count.per.region.orientation), size=theme.text.size.within, inherit.aes = F)
#p.region.orientation.hbarplot

p.region.ukborn.hbarplot <- ggplot(PHE.geo.UKborn, aes(Count,phe_centre,fill=ukborn)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="UK Born",values=PHE.ukborn.cols$ukborn.cols, breaks=PHE.ukborn.cols$ukborn) +
  labs(y="UKHSA Region", x="UK Born") +
  guides(fill=guide_legend(nrow=3)) +
  geom_text(data=PHE.geo.UKborn, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.region.ukborn.hbarplot

p.region.London.hbarplot <- ggplot(PHE.geo.London, aes(Count,phe_centre,fill=london)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="London",values=PHE.london.cols$london.cols, breaks=PHE.london.cols$london) +
  labs(y="UKHSA Region", x="London") +
  guides(fill=guide_legend(nrow=3)) +
  geom_text(data=PHE.geo.London, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.region.London.hbarplot

p.region.Age.hbarplot <- ggplot(PHE.geo.Age, aes(Count,phe_centre,fill=age_group)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="Age\nGroup",values=PHE.Age.cols$age_group.cols, breaks=PHE.Age.cols$age_group) +
  labs(y="UKHSA Region", x="Age Group") +
  guides(fill=guide_legend(ncol=1)) +
  geom_text(data=PHE.geo.Age, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.region.Age.hbarplot


Combined plot

PHE.region.combiplot.1 <- plot_grid(p.region.year.bubbleplot, p.region.hbarplot + y.theme.strip, p.region.orientation.hbarplot + y.theme.strip, p.region.hiv.hbarplot + y.theme.strip, p.region.Age.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(4,2,2,2,2), scale=0.9)

PHE.region.combiplot.1


Regions as a complex multipanel plot



# legends
PHE.region.combiplot.1.legends <- plot_grid(get_legend(p.region.year.bubbleplot), get_legend(p.region.hbarplot + y.theme.strip), get_legend(p.region.orientation.hbarplot + y.theme.strip), get_legend(p.region.hiv.hbarplot + y.theme.strip), get_legend(p.region.Age.hbarplot + y.theme.strip), nrow=1, align="h", rel_widths=c(6,4,4,4,4), scale=0.95)


# Arrange plots vertically
p.year.bubbleplot.combi <- plot_grid(p.all.year.bubbleplot + x.theme.strip, p.region.year.bubbleplot + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))

p.region.hbar.counts.combi <- plot_grid(p.all.hbarplot + x.theme.strip + y.theme.strip, p.region.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))

p.region.hbar.orientation.combi <- plot_grid(p.all.orientation.hbarplot + x.theme.strip + y.theme.strip, p.region.orientation.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))

p.region.hbar.hiv.combi <- plot_grid(p.all.hiv.hbarplot + x.theme.strip + y.theme.strip, p.region.hiv.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))

p.region.hbar.Age.combi <- plot_grid(p.all.Age.hbarplot + x.theme.strip + y.theme.strip, p.region.Age.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))

# Combine the plots
p.region.hbar.combi.plus.all <- plot_grid(p.year.bubbleplot.combi, p.region.hbar.counts.combi, p.region.hbar.orientation.combi, p.region.hbar.hiv.combi, p.region.hbar.Age.combi, nrow=1, rel_widths=c(6,4,4,4,4), labels = c("A","B","C","D","E"), label_size=panel.lab.size, vjust=0.25)
# and add the legends on top
p.region.hbar.combi.plus.all.with.legends <- plot_grid(p.region.hbar.combi.plus.all, PHE.region.combiplot.1.legends, ncol=1, rel_heights=c(6,1), scale = 0.95)



p.region.hbar.combi.plus.all.with.legends

#ggsave(paste0(Figure_output_directory, "SupFig2_TPA-PHE_Sample-metadistros-by-phe_region+all-combi.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=240, height=135, device='pdf', dpi=1200)



Now lets look at some genetic data
### Make ML tree with sublineage tippoints

TPA.MLtree.ggtree.tippoint <- TPA.MLtree.ggtree %<+% data.frame(Sample_Name=TPA.meta2.1$Sample_Name, Sublineage=TPA.meta2.1$TPA.pinecone.sublineage,stringsAsFactors = F) + 
  geom_tippoint(aes(color=Sublineage), size=0.5, alpha=0.5, show.legend = FALSE) + 
  scale_color_manual(name="Sublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage)


Add metadata

# Continent
p.TPA.MLtree.PHE <- gheatmap(TPA.MLtree.ggtree.tippoint,
               TPA.rawseq.continents.p, color=NULL,width=0.075,offset=0.00000025, colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) + 
  scale_fill_manual(name="Continent",values=continental.cols.brew2$continent.col, breaks=continental.cols.brew2$Continent, guide = guide_legend(order = 1,ncol=2)) +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
  new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
# is UK
p.TPA.MLtree.PHE <- gheatmap(p.TPA.MLtree.PHE,
               TPA.rawseq.UK.p, color=NULL,width=0.075,offset=0.00001025, colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) + 
  scale_fill_manual(name="England/Other", values=c("black","grey95"), breaks=c("England","Other"), guide = guide_legend(order = 2,ncol=2)) +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
  new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
# Lineage
p.TPA.MLtree.PHE <- gheatmap(p.TPA.MLtree.PHE,TPA.rawseq.Lineage.p, color=NULL,width=0.075,offset=0.00002025, colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) + 
  scale_fill_manual(name="Lineage",values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage, guide = guide_legend(order = 3, ncol=2)) + theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
   new_scale_fill() +
  NULL
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
# sublineage
p.TPA.MLtree.PHE <- gheatmap(p.TPA.MLtree.PHE, data.frame(row.names=TPA.meta2.1$Sample_Name, Sublineage=TPA.meta2.1$TPA.pinecone.sublineage,stringsAsFactors = F), color=NULL,width=0.075,offset=0.00003025, colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) + 
  scale_fill_manual(name="Sublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage, guide = guide_legend(order = 4, ncol=3)) + theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
   new_scale_fill() +
  NULL
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.


plot

p.TPA.MLtree.PHE


#ggsave(paste0(Figure_output_directory, "SupFig3_TPA-PHE_Global_Phylo+UK-highlights.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=185, height=160, device='pdf', dpi=1200)



### Geographic distributions of Lineages and Sublineages What about sublineages?

p.region.Lineage.hbarplot <- ggplot(PHE.geo.Lineage, aes(Count,phe_centre,fill=TPA_Lineage)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="TPA\nLineage",values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage) +
  labs(y="UKHSA Region", x="TPA Lineage") +
  guides(fill=guide_legend(nrow=3)) +
  #geom_text(data=PHE.geo.Lineage, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F) +
  NULL

p.region.sublineage.hbarplot <- ggplot(PHE.geo.sublineage, aes(Count,phe_centre,fill=TPA.pinecone.sublineage)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="TPA\nSublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage) +
  labs(y="UKHSA Region", x="TPA Sublineage") +
  guides(fill=guide_legend(nrow=4)) +
  #geom_text(data=PHE.geo.sublineage, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F) +
  NULL


Combi plot (geography lineages)

PHE.region.combiplot.2.lineages <- plot_grid(p.region.year.bubbleplot +legend.strip, p.region.hbarplot + y.theme.strip + legend.strip + coord_cartesian(xlim=c(0,150)), p.region.Lineage.hbarplot + y.theme.strip +legend.strip, p.region.sublineage.hbarplot + y.theme.strip +legend.strip, nrow=1, align="h", rel_widths=c(6,3,4,4), scale=0.99, labels=c("C","D","E","F"), label_size=panel.lab.size)
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
# separate out the plot for the legends
p.region.year.bubbleplot.legend <- get_legend(p.region.year.bubbleplot)
p.region.hbarplot.legend <- get_legend(p.region.hbarplot + y.theme.strip)
p.region.Lineage.hbarplot.legend <- get_legend(p.region.Lineage.hbarplot + y.theme.strip)
p.region.sublineage.hbarplot.legend <- get_legend(p.region.sublineage.hbarplot + y.theme.strip)

PHE.region.combiplot.2.lineages.legend <- plot_grid(p.region.year.bubbleplot.legend, p.region.hbarplot.legend, p.region.Lineage.hbarplot.legend, p.region.sublineage.hbarplot.legend, nrow=1, align="h", rel_widths=c(6,3,4,4))

PHE.region.combiplot.2.lineages <- plot_grid(PHE.region.combiplot.2.lineages, PHE.region.combiplot.2.lineages.legend, rel_heights = c(4,1), ncol=1)

PHE.region.combiplot.2.lineages


OK, let’s now add a map of these geographical distributions


Let’s used ONS published shape files - there is one available that shows Public Health England region boundaries.


# Generate approximate regional GPS coords
PHE.region.GPS <- data.frame(
  stringsAsFactors = FALSE,
          phe_centre = c("East Midlands",
                         "East of England","London","North East","North West",
                         "South East","South West","West Midlands",
                         "Yorkshire and Humber","UK (not England)","Not Known"),
            Longitude = c(-0.7,0.5,-0.2,-1.9,-2.4,
                         0.05,-2.9,-2,-0.8,0.1,0.63),
           Latitude = c(52.9,52.4,51.5,55,53.7,
                         51.1,51,52.6,53.8,54.7,54.1)
  )  
PHE.region.GPS <- left_join(PHE.region.GPS, PHE.geo.Lineage[PHE.geo.Lineage$TPA_Lineage=="SS14",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS)[4] <- "SS14"
PHE.region.GPS <- left_join(PHE.region.GPS, PHE.geo.Lineage[PHE.geo.Lineage$TPA_Lineage=="Nichols",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS)[5] <- "Nichols"
PHE.region.GPS[is.na(PHE.region.GPS)] <- 0

PHE.region.GPS <- left_join(PHE.region.GPS, PHE.geo.Lineage[PHE.geo.Lineage$TPA_Lineage=="SS14",c("phe_centre","total.region")], by="phe_centre")
colnames(PHE.region.GPS)[6] <- "Region_Count"

PHE.region.GPS$radius <- 0.5*(1-1/sqrt(PHE.region.GPS$Region_Count))


###############################
# Import datafile from https://geoportal.statistics.gov.uk/datasets/public-health-england-centres-december-2016-full-clipped-boundaries-in-england/explore?location=52.950000%2C-2.000000%2C6.88

UK.shapefile <- readOGR(dsn=UK.publichealth.shapefile.data)
Warning in OGRSpatialRef(dsn, layer, morphFromESRI = morphFromESRI, dumpSRS = dumpSRS,  :
  Discarded datum Ordnance_Survey_of_Great_Britain_1936 in Proj4 definition: +proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +units=m +no_defs
OGR data source with driver: ESRI Shapefile 
Source: "/Users/mb29/Papers/Treponema_UK-PHE-gen-epi_2021/Github/Syphilis_Genomic_Epi_England_2022-23/inputdata/Public_Health_England_Centres_(December_2016)_Boundaries", layer: "Public_Health_England_Centres_(December_2016)_Boundaries"
with 9 features
It has 9 fields
#Reshape for ggplot2 using the Broom package
UK.mapdata <- tidy(UK.shapefile, region="phec16nm")

#UK.gg <- ggplot() + geom_polygon(data = UK.mapdata, aes(x = long, y = lat, group = group), color = "#FFFFFF", size = 0.25)
UK.gg <- ggplot() + geom_polygon(data = UK.mapdata, aes(x = long, y = lat, group = group), color="grey25", fill="grey90", size = 0.075)

#UK.gg <- UK.gg + coord_fixed(1) + theme_nothing()
#UK.gg
# Map plotting file becomes _very_ big - use ggrastr to reduce the size
UK.gg <-ggplot() + ggrastr::rasterise(geom_polygon(data = UK.mapdata, aes(x = long, y = lat, group = group), color="grey25", fill="grey90", size = 0.075), dpi=400) + coord_fixed(1) + theme_nothing()

#rasterise(geom_point(aes(carat, price, colour = cut), data=diamonds), dpi=30)



# Convert UK regions to be compatible with map
# First find centre point for each region
UK.mapdata.regions.meancoords <- UK.mapdata %>% dplyr::group_by(id) %>%
  dplyr::summarise(mean.lat=mean(lat), mean.long=median(long)) %>%
  dplyr::ungroup()
colnames(UK.mapdata.regions.meancoords)[1] <- "phe_centre"

PHE.region.GPS.ukmap <- dplyr::left_join(PHE.region.GPS, UK.mapdata.regions.meancoords, by="phe_centre")

# Add artificial location for 'not known'
PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre=="Not Known","mean.lat"] <- 600000
PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre=="Not Known","mean.long"] <- 550000

# Shift "South East" slightly to reduce the overlap with London
PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre=="South East","mean.long"] <- 475000
# Shift "East of England East" slightly to reduce the overlap with London 
PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre=="East of England","mean.lat"] <- 275000

# Not going to try plotting the 2 samples from elsewhere in the UK, so remove that row
PHE.region.GPS.ukmap <- PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre != "UK (not England)",]

# Create radius variable for plotting pie sizes (use log10(n)*20,000)
PHE.region.GPS.ukmap$radius.UK <- log10(PHE.region.GPS.ukmap$Region_Count)*20000

#PHE.geo.count.years.lineage

UK.gg.scatterpie <- UK.gg + geom_scatterpie(data=PHE.region.GPS.ukmap, aes(mean.long, mean.lat, group=phe_centre, r=radius.UK), alpha=0.85, color=NA, cols=c("Nichols","SS14")) + 
  scale_fill_manual(name="TPA\nLineage",values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage) + theme(legend.position="top")

UK.gg.scatterpie <- UK.gg.scatterpie + geom_scatterpie_legend(PHE.region.GPS.ukmap[!is.na(PHE.region.GPS.ukmap$mean.lat),"radius.UK"], labeller=function(x) round((10^(x/20000)),0), n=3, x=150000, y=500000)

UK.gg.scatterpie <- UK.gg.scatterpie + theme_nothing()

#? Add labels
UK.gg.scatterpie.labs <- UK.gg.scatterpie + geom_label_repel(data=PHE.region.GPS.ukmap[!is.na(PHE.region.GPS.ukmap$mean.lat),], aes(mean.long, mean.lat, label=phe_centre), size=theme.text.size.within, nudge_x = 50000, nudge_y = -25000, segment.size  = 0.1) + theme(legend.key.size = unit(0.55,"line"), legend.position="bottom") + 
  theme.text.size +
  theme_nothing()

UK.gg.scatterpie.labs



Now do an equivalent plot for sublineages

PHE.region.GPS.ukmap.sublin <- PHE.region.GPS.ukmap


PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="1",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[11] <- "1"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="2",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[12] <- "2"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="3",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[13] <- "3"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="6",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[14] <- "6"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="8",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[15] <- "8"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="14",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[16] <- "14"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="15",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[17] <- "15"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="16",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[18] <- "16"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="Singleton",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[19] <- "Singleton"
PHE.region.GPS.ukmap.sublin[is.na(PHE.region.GPS.ukmap.sublin)] <- 0

# Most samples are either sublineage 1 or 14. Let's create a count of samples that are neither.
PHE.region.GPS.ukmap.sublin$`Other Sublineages` <- sapply(1:nrow(PHE.region.GPS.ukmap.sublin), function (x) PHE.region.GPS.ukmap.sublin$Region_Count[x]-sum(PHE.region.GPS.ukmap.sublin$`1`[x], PHE.region.GPS.ukmap.sublin$`14`[x])) 



UK.gg.scatterpie.sublineage <- UK.gg + geom_scatterpie(data=PHE.region.GPS.ukmap.sublin[PHE.region.GPS.ukmap.sublin$mean.long!=0,], aes(mean.long, mean.lat, group=phe_centre, r=radius.UK), alpha=0.85, color=NA, cols=c("1","14","Other Sublineages")) + 
  scale_fill_manual(name="TPA\nSublineage",values=c("#FC9272","#BCBDDC", "grey50"), breaks=c("1","14","Other Sublineages"))

# add legend
UK.gg.scatterpie.sublineage <- UK.gg.scatterpie.sublineage + geom_scatterpie_legend(PHE.region.GPS.ukmap[!is.na(PHE.region.GPS.ukmap$mean.lat),"radius.UK"], labeller=function(x) round((10^(x/20000)),0), n=3, x=150000, y=500000)

#UK.gg.scatterpie <- UK.gg.scatterpie + x.theme.strip + y.theme.strip
UK.gg.scatterpie.sublineage <- UK.gg.scatterpie.sublineage + theme_nothing()

#? Add labels
UK.gg.scatterpie.sublineage <- UK.gg.scatterpie.sublineage + geom_label_repel(data=PHE.region.GPS.ukmap[!is.na(PHE.region.GPS.ukmap$mean.lat),], aes(mean.long, mean.lat, label=phe_centre), size=theme.text.size.within, nudge_x = 50000, nudge_y = -25000, segment.size  = 0.1) +
  theme(legend.key.size = unit(0.55,"line"), legend.position="bottom") + 
  theme.text.size +
  theme_nothing()


UK.gg.scatterpie.sublineage


Combined map plot

UK.gg.scatterpie.combi <- plot_grid(UK.gg.scatterpie.labs, UK.gg.scatterpie.sublineage, ncol=2, labels = c("A","B"), label_size=panel.lab.size)

UK.gg.scatterpie.combi



Plot in combination with barplots

plot_grid(UK.gg.scatterpie.combi, PHE.region.combiplot.2.lineages, nrow=2, rel_heights=c(4,5))


#ggsave(paste0(Figure_output_directory,"Fig2_TPA-PHE_Map-Lineage+Barplots.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=190, height=185, device='pdf', dpi=1200)



### Analysis by sublineage
Now lets start exploring how samples are distributed by sublineage

PHE.metadata.linked <- PHE.metadata.linked
PHE.metadata.linked$TPA.pinecone.sublineage <- factor(PHE.metadata.linked$TPA.pinecone.sublineage, levels=rev(as.character(sort(unique(PHE.metadata.linked$TPA.pinecone.sublineage)))))

PHE.Lineage.count <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA_Lineage) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total=sum(Count), perc=(Count/total)*100)

PHE.sublin.count <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA.pinecone.sublineage) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total=sum(Count), perc=(Count/total)*100)

PHE.geo.sublin.years <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA.pinecone.sublineage,year) %>%
  dplyr::summarise(Count=n())
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
## Generate some stats about sublineage groups

# Generate some stats about gender orientation
PHE.sublineage.orientation.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA.pinecone.sublineage,gender_orientation) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.sublin=sum(Count)) %>%
  dplyr::arrange(desc(gender_orientation), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Generate some stats about UK born
PHE.sublineage.UKborn <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA.pinecone.sublineage, ukborn) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.sublin=sum(Count)) %>%
  #dplyr::arrange(desc(ukborn), .by_group=T) %>%
  dplyr::arrange(desc(ukborn), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
  
# Generate some stats about London based
PHE.sublineage.London <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA.pinecone.sublineage, london) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.sublin=sum(Count)) %>%
  dplyr::arrange(desc(london), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Generate some stats about Age group
PHE.sublineage.Age <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA.pinecone.sublineage, age_group) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.sublin=sum(Count)) %>%
  dplyr::arrange(desc(age_group), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Generate some stats about HIV group
PHE.sublineage.HIV <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA.pinecone.sublineage, hivpos) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.sublin=sum(Count)) %>%
  dplyr::arrange(desc(hivpos), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Generate some stats by PHE Region
PHE.sublineage.PHEcentre <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA.pinecone.sublineage, phe_centre) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.sublin=sum(Count)) %>%
  dplyr::arrange(desc(phe_centre), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.


Plot by sublineage

p.sublineage.year.bubbleplot <- ggplot(PHE.geo.sublin.years, aes(as.numeric(year), TPA.pinecone.sublineage, colour=TPA.pinecone.sublineage)) +
  geom_point(alpha=0.65, aes(size=Count)) + 
  geom_line(alpha=0.25) +
  guides(colour='none') +
  scale_size_area(max_size = 7,breaks=c(1,5,10,25,50)) + 
  guides(size=guide_legend(nrow=2, direction = 'horizontal', byrow=T)) +
  theme_light() +
  scale_color_manual(name="TPA\nSublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage) +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  labs(y="TPA Sublineage", x="Sample Year", size="Count") 
#p.sublineage.year.bubbleplot

p.sublineage.hbarplot <- ggplot(PHE.sublin.count, aes(Count,TPA.pinecone.sublineage,fill=TPA.pinecone.sublineage)) +
  geom_barh(stat="identity", position="stack", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="TPA\nSublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage) +
  labs(y="TPA Sublineage", x="Sample Count") +
  geom_text(data=PHE.sublin.count, aes((Count+12), TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F) +
  #coord_cartesian(xlim=c(0,200)) +
  coord_cartesian(xlim=c(0,260)) +
  guides(fill=guide_legend(ncol=2))
#p.sublineage.hbarplot 

p.sublineage.orientation.hbarplot <- ggplot(PHE.sublineage.orientation.counts, aes(y=TPA.pinecone.sublineage,x=Count,fill=gender_orientation)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="Orientation",values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) +
  labs(y="TPA Sublineage", x="Orientation") +
  guides(fill=guide_legend(ncol=1)) +
  geom_text(data=PHE.sublineage.orientation.counts, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.region.orientation.hbarplot

p.sublineage.hiv.hbarplot <- ggplot(PHE.sublineage.HIV, aes(y=TPA.pinecone.sublineage, x=Count,fill=hivpos)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="HIV +ve",values=PHE.hiv.cols$hiv.cols, breaks=PHE.hiv.cols$hivpos) +
  labs(y="TPA Sublineage", x="HIV +ve") +
  guides(fill=guide_legend(ncol=1)) + 
  geom_text(data=PHE.sublineage.HIV, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.sublineage.hiv.hbarplot

p.sublineage.ukborn.hbarplot <- ggplot(PHE.sublineage.UKborn, aes(y=TPA.pinecone.sublineage,x=Count,fill=ukborn)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="UK\nborn",values=PHE.ukborn.cols$ukborn.cols, breaks=PHE.ukborn.cols$ukborn) +
  labs(y="TPA Sublineage", x="UK born") +
  guides(fill=guide_legend(nrow=3)) +
  geom_text(data=PHE.sublineage.UKborn, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.sublineage.ukborn.hbarplot

p.sublineage.Age.hbarplot <- ggplot(PHE.sublineage.Age, aes(y=TPA.pinecone.sublineage, x=Count ,fill=age_group)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="Age\nGroup",values=PHE.Age.cols$age_group.cols, breaks=PHE.Age.cols$age_group) +
  labs(y="TPA Sublineage", x="Age Group") +
  guides(fill=guide_legend(ncol=1)) +
  geom_text(data=PHE.sublineage.Age, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.sublineage.Age.hbarplot


p.sublineage.PHEregion.hbarplot <- ggplot(PHE.sublineage.PHEcentre, aes(y=TPA.pinecone.sublineage, x=Count, fill=phe_centre)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="UKHSA\nRegion",values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$PHE.region) +
  labs(y="TPA Sublineage", x="UKHSA Region") +
  guides(fill=guide_legend(nrow=4)) +
  geom_text(data=PHE.sublineage.PHEcentre, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)


Look at how sublineages are distributed by region (sublineage-centric)

p.sublineage.PHEregion.hbarplot


Combine patient metadata into a plot

#PHE.sublineages.combiplot.1 <- plot_grid(p.sublineage.year.bubbleplot, p.sublineage.hbarplot + y.theme.strip, p.sublineage.orientation.hbarplot + y.theme.strip, p.sublineage.hiv.hbarplot + y.theme.strip, p.sublineage.PHEregion.hbarplot + y.theme.strip, p.sublineage.ukborn.hbarplot + y.theme.strip, p.sublineage.Age.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(3,2,2,2,2,2,2), scale=0.9)

#PHE.sublineages.combiplot.1 <- plot_grid(p.sublineage.year.bubbleplot, p.sublineage.hbarplot + y.theme.strip, p.sublineage.orientation.hbarplot + y.theme.strip, p.sublineage.hiv.hbarplot + y.theme.strip, p.sublineage.Age.hbarplot + y.theme.strip, p.sublineage.PHEregion.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(3,2,2,2,2,4), scale=0.9)

PHE.sublineages.combiplot.1 <- plot_grid(p.sublineage.year.bubbleplot, p.sublineage.hbarplot + y.theme.strip, p.sublineage.orientation.hbarplot + y.theme.strip, p.sublineage.hiv.hbarplot + y.theme.strip, p.sublineage.Age.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(4,2,2,2,2), scale=0.9)

PHE.sublineages.combiplot.1 


Lets add the ‘all’ row again to the ‘by sublineage’ plot

# legends
PHE.sublineage.combiplot.1.legends <- plot_grid(get_legend(p.sublineage.year.bubbleplot), get_legend(p.sublineage.hbarplot + y.theme.strip), get_legend(p.sublineage.orientation.hbarplot + y.theme.strip), get_legend(p.sublineage.hiv.hbarplot + y.theme.strip), get_legend(p.sublineage.Age.hbarplot + y.theme.strip), nrow=1, align="h", rel_widths=c(6,4,4,4,4), scale=0.95)

# regions
#PHE.sublineage.combiplot.1.nolegend <- plot_grid(p.sublineage.year.bubbleplot + legend.strip, p.sublineage.hbarplot + y.theme.strip + legend.strip, p.sublineage.orientation.hbarplot + y.theme.strip + legend.strip, p.sublineage.hiv.hbarplot + y.theme.strip + legend.strip, p.sublineage.Age.hbarplot + y.theme.strip + legend.strip, nrow=1, align="h", rel_widths=c(4,2,2,2,2), scale=0.9)

# Or do it vertically
p.sublineage.year.bubbleplot.combi <- plot_grid(p.all.year.bubbleplot + x.theme.strip, p.sublineage.year.bubbleplot + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))

p.sublineage.hbar.counts.combi <- plot_grid(p.all.hbarplot + x.theme.strip + y.theme.strip, p.sublineage.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))

p.sublineage.hbar.orientation.combi <- plot_grid(p.all.orientation.hbarplot + x.theme.strip + y.theme.strip, p.sublineage.orientation.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))

p.sublineage.hbar.hiv.combi <- plot_grid(p.all.hiv.hbarplot + x.theme.strip + y.theme.strip, p.sublineage.hiv.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))

p.sublineage.hbar.Age.combi <- plot_grid(p.all.Age.hbarplot + x.theme.strip + y.theme.strip, p.sublineage.Age.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))

# Combine the plots
p.sublineage.hbar.combi.plus.all <- plot_grid(p.sublineage.year.bubbleplot.combi, p.sublineage.hbar.counts.combi, p.sublineage.hbar.orientation.combi, p.sublineage.hbar.hiv.combi, p.sublineage.hbar.Age.combi, nrow=1, rel_widths=c(7,3,4,4,4), labels=c("A", "B", "C", "D", "E"),label_size=panel.lab.size, vjust=1, scale=0.99)

# and add the legends on top
#p.sublineage.hbar.combi.plus.all.with.legends <- plot_grid(PHE.sublineage.combiplot.1.legends, p.sublineage.hbar.combi.plus.all, ncol=1, rel_heights=c(1,9))

# legends below
p.sublineage.hbar.combi.plus.all.with.legends <- plot_grid(p.sublineage.hbar.combi.plus.all, PHE.sublineage.combiplot.1.legends, ncol=1, rel_heights=c(8,1))


p.sublineage.hbar.combi.plus.all.with.legends


  These patterns look fairly similar between sublineages, and (apart from 1 & 14) the groups are very small. However, sublineage 14 does appear to have a higher proportion of MSM compared to sublineage 1 and others. Let’s test that formally using 2x2 fisher’s tests

PHE.MSM.counts.all <- PHE.metadata.linked %>% 
  dplyr::group_by(is.MSM, .drop=F) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.sublin=sum(Count)) %>%
  dplyr::arrange((is.MSM), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))

PHE.sublineage.MSM.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA.pinecone.sublineage,is.MSM, .drop=F) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.sublin=sum(Count)) %>%
  dplyr::arrange((is.MSM), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) #%>%
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
  #dplyr::filter(!is.na(is.MSM))


PHE.sublineage.MSM.counts.wider <- PHE.sublineage.MSM.counts %>% dplyr::select(TPA.pinecone.sublineage, is.MSM, Count) %>%
  tidyr::pivot_wider(names_from = is.MSM, values_from=Count) %>%
  dplyr::mutate(MSM=replace_na(MSM, 0), Other=replace_na(Other, 0), Total=sum(MSM,Other)) %>%
  #dplyr::select(-`NA`) %>%
  dplyr::filter(Total!=0)
  

PHE.sublineage.MSM.pval <- data.frame(TPA.pinecone.sublineage=PHE.sublineage.MSM.counts.wider$TPA.pinecone.sublineage, p.fisher=sapply(1:nrow(PHE.sublineage.MSM.counts.wider), function (x) fisher.test(matrix(as.numeric(c(PHE.sublineage.MSM.counts.wider[x,"MSM"],
                                PHE.sublineage.MSM.counts.wider[x,"Other"],
                                PHE.MSM.counts.all[PHE.MSM.counts.all$is.MSM=="MSM","Count"], PHE.MSM.counts.all[PHE.MSM.counts.all$is.MSM=="Other","Count"])),nrow=2))[[1]]), stringsAsFactors=F)

PHE.sublineage.MSM.counts.wider <- dplyr::left_join(PHE.sublineage.MSM.counts.wider, PHE.sublineage.MSM.pval, by="TPA.pinecone.sublineage")

PHE.sublineage.MSM.counts.wider



### Visualisation of UK genomic relationships
Ok, let’s make a tree for displaying these relationships using the UK dataset only
From some experimentation, a ‘GrapeTree’ minimum spanning network works well for visualising the clonality of these populations. We can use a SNP-scaled phylogeny as direct input to GrapeTree, and this will allow branches to be scaled appropriately. However, although annotation is allowed within the GrapeTree software, colours must be manually edited. Final GrapeTree plots can then be imported back into R for combining with other plots.

Alternative visualisations - grapetree?
Take the 526-global phylogeny (snp-scaled version from pyjar), and prune to only include the UK strains from this study - this ensures the topology is consistent accross studies.


TPA.pyjar.tree.subset.uk <- ape::keep.tip(TPA.pyjar.tree, as.character(unlist(PHE.metadata.linked[PHE.metadata.linked$Geo_Country=="UK","Sample_Name"])))


TPA.pyjar.tree.subset.global_beast_only.seqlanes <- TPA.meta2.1 %>% filter(full.temporal.analysis=='Yes') %>%
  select(Cleaned_fastq_id) %>% pull()

TPA.pyjar.tree.subset.uk.seqlanes <- as.character(unlist(PHE.metadata.linked[PHE.metadata.linked$Geo_Country=="UK","Cleaned_fastq_id"]))


ggtree(TPA.pyjar.tree.subset.uk)

#write.tree(TPA.pyjar.tree.subset.uk, paste0(Data_input_directory,"TPA.UK-only.pyjar.2022-02-03.tre"))

# Write out a metadata sheet for the relevant information
PHE.metadata.linked.grapetree <- PHE.metadata.linked[,c("Sample_Name", "year","gender_orientation","phe_centre","hivpos","ukborn","TPA_Lineage","TPA.pinecone.sublineage")]
colnames(PHE.metadata.linked.grapetree)[1] <- "ID"

#write.table(PHE.metadata.linked.grapetree, paste0(Data_input_directory,"TPA.UK-only.grapetree.meta.2022-02-03.tsv"), sep = "\t", quote=F, row.names = F)


Tree independently visualised and annotated using GrapeTree.
Now import and integrate GrapeTree plot with metadata plots.

ggsave(plot=p.sublineage.hbar.combi.plus.all.with.legends.B2F.with.grapetree, paste0(Figure_output_directory, "Fig1_TPA-PHE_Sample-distros-sublineage.",format(Sys.Date(),"%Y%m%d"),".svg"), units='mm', width=190, height=185, device='svglite', dpi=1200)
Error in `plot_dev()`:
! Unknown graphics device 'svglite'
Backtrace:
 1. ggplot2::ggsave(...)
 2. ggplot2:::plot_dev(device, filename, dpi = dpi)


Manage other GrapeTree plots (for consistency)

TPA-UK-2022-02-16.-MSTree_3-way-figure.Inscaped-2

# Bring in 3-way graphetree plot (3 different metadata variables using the same input tree)
TPA.UK.Grapetree.3way <- ggdraw() + draw_image(TPA.UK.Grapetree.3way.file)
TPA.UK.Grapetree.3way


#ggsave(paste0(Figure_output_directory, "SupFig4_TPA-PHE_Grapetree-3ways.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=145, height=180, device='pdf', dpi=1200)


And also do the HIV status plot


TPA.UK.Grapetree.HIV <- ggdraw() + draw_image(TPA.UK.Grapetree.HIV.file)
TPA.UK.Grapetree.HIV


#ggsave(paste0(Figure_output_directory, "SupFig5_TPA-PHE_Grapetree-HIV.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=185, height=110, device='pdf', dpi=1200)



### Phylogenetic context analyses
Ok, now lets look at some trees
First, let’s formalise BEAST tree plotting as three separate functions to enable other trees to be plotted the same way

full.beast2.tree <- read.beast(full.beast2.tree.file)
full.beast2.tree@phylo$tip.label <- gsub("\\|.+$","",full.beast2.tree@phylo$tip.label, perl=T)

################################################################################################
# function to extract a tree based on sublineage
Extract_sublineage_tree_for_plot <- function(my.beast.tree, my.metadata, my.phe.meta, my.sublineage){
  # get all tips to include from metadata, then calculate MRCA from tree
  sublineage.test.mrca <- getMRCA(my.beast.tree@phylo, as.character(unlist(my.metadata[my.metadata$TPA.pinecone.sublineage==my.sublineage,"Sample_Name"])))
  ######
  TPA.beast.subtree.test <- tree_subset(my.beast.tree, node=sublineage.test.mrca, levels_back=0)
  return(TPA.beast.subtree.test)
}
#Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 1)
################################################################################################


################################################################################################
# Function to prepare a beast tree with timescale indicators, posterior support and 95% HPD bars
plot_beast_subtree_with_HPD <- function(my.beast.tree, my.metadata, my.phe.meta, mrsd.fulltree){
  # get MRCD for tree
  mrsd.Beast.tree.test.s <- max(as.numeric(unlist(my.metadata[my.metadata$Sample_Name %in% my.beast.tree@phylo$tip.label,"Sample_Year"])))
  mrsd.Beast.tree.test <- lubridate::ymd(paste0(mrsd.Beast.tree.test.s,"-06-01")) 
  mrsd.Beast.tree.fulltree <- lubridate::ymd(mrsd.fulltree) 
  #mrsd.Beast.tree.test
  # plot basic tree
  options(ignore.negative.edge=TRUE)
  p.TPA.beast.subtree.test <- ggtree(my.beast.tree, mrsd=mrsd.Beast.tree.test, ladderize = T, size=0.4) + scale_x_continuous(breaks=seq(1960,2020,10), minor_breaks=seq(2000, 2020, 1)) +
    theme_tree2() +
    # Add date lines for easy interpretation  
    theme(panel.grid.major   = element_line(color="grey50", size=.2),
          panel.grid.minor   = element_line(color="grey85", size=.2),
          panel.grid.major.y = element_blank(),
          panel.grid.minor.y = element_blank())
  # Add posterior support as node points
  p.TPA.beast.subtree.test <- p.TPA.beast.subtree.test + geom_point2(aes(subset=(!isTip & as.numeric(posterior)>0.8)),color="gray60",size=2,alpha=0.5, shape=18) + 
    geom_point2(aes(subset=(!isTip & as.numeric(posterior)>0.91)),color="gray40",size=3,shape=18,alpha=0.5) + 
    geom_point2(aes(subset=(!isTip & as.numeric(posterior)>=0.96)),color="black",size=3,shape=18,alpha=0.5)
  ######
  # extract 95% HPD intervals - geom_range seems unable to do correctly with this tree (known bug for tip dated trees), so extract data and plot using geom_segment
  TPA.beast.subtree.test.data <- fortify(my.beast.tree)
  minmax <- t(matrix(unlist(TPA.beast.subtree.test.data[!is.na(TPA.beast.subtree.test.data$height_0.95_HPD),"height_0.95_HPD"]),nrow=2))
  bar_df <- data.frame(node_id=TPA.beast.subtree.test.data[!is.na(TPA.beast.subtree.test.data$height_0.95_HPD),"node"],as.data.frame(minmax))
  names(bar_df) <- c('node_id','min','max') 
  bar_df <- bar_df %>% filter(node_id > Ntip(my.beast.tree@phylo))
  bar_df <- bar_df %>% left_join(TPA.beast.subtree.test.data, by=c('node_id'='node')) #%>% select(node_id,min,max,y)
  #mrcd.decimal <- decimal_date(mrsd.Beast.tree.test)
  mrcd.decimal <- decimal_date(mrsd.Beast.tree.fulltree)
  
  # Now add HPDs to plot
  p.TPA.beast.subtree.test <- p.TPA.beast.subtree.test + geom_segment(aes(x=mrcd.decimal-max, y=y, xend=mrcd.decimal-min, yend=y), data=bar_df, color='red', alpha=0.2, size=2.0)
  # Output tree 
  return(p.TPA.beast.subtree.test)
}
################################################################################################


################################################################################################
# Function to add metadata to tree
# Has two optional arguments "initial.track.offset" and "track.scaling" which can be used to alter the width and positioning of metadata tracks

plot_beast_subtree_with_PHE_metadata <- function(my.beast.tree.input, my.metadata, my.phe.meta, initial.track.offset, track.scaling){
    # Add code to allow scaling up of the track offsets and widths - useful for much bigger length trees
  if(missing(initial.track.offset)){
    initial.track.offset <- 0
  }    
  if(missing(track.scaling)){
    track.scaling <- 1
  }
  # Calculate amount to offset each heatmap track
  offset.dist <- 4*track.scaling
  track.width <- (1/max(my.beast.tree.input$data$height)*3)*track.scaling
  
  # make a list of taxa used in this plot 
  my.taxa.list <- as.character(unlist(filter(my.beast.tree.input$data, isTip==TRUE) %>% select(label)))
  
  # make a color scale for sampling years
  #PHE.sublintest.year.cols <- data.frame(year=sort(unique(as.numeric(unlist(my.metadata[(my.metadata$Sample_Name %in% my.taxa.list),"Sample_Year"],use.names=F)))),stringsAsFactors = T)
  #PHE.sublintest.year.cols$year.cols <- colorRampPalette(brewer.pal(7, "YlOrRd"))(nrow(PHE.sublintest.year.cols))
  
  # Or alternatively, use a common colour scheme for all data (maybe more sensible)
  PHE.sublintest.year.cols <- data.frame(year=TPA.year.cuttoff.cols$date.cuttoff, year.cols=TPA.year.cuttoff.cols$date.cuttoff.col, stringsAsFactors = F)
  
  # make metadata file for UK regions present in sublineage
  sublin.test.region.meta <- data.frame(row.names=as.character(unlist(my.phe.meta[my.phe.meta$Sample_Name %in% my.taxa.list,"Sample_Name"])), Region=as.character(unlist(my.phe.meta[my.phe.meta$Sample_Name %in% my.taxa.list,"phe_centre"])), stringsAsFactors = F)
  
  # Add heatmap strips
  # Sample Year
  #TPA.beast.subtree.test.global.plot1.regional <- gheatmap(my.beast.tree.input, TPA.rawseq.all.Years.p, color=NULL,width=track.width, offset=initial.track.offset+offset.dist,colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
    #scale_fill_manual(name="Year", values=PHE.sublintest.year.cols$year.cols,breaks=PHE.sublintest.year.cols$year, guide = guide_legend(order = 1, ncol=2)) +
    #ggnewscale::new_scale_fill()
  TPA.beast.subtree.test.global.plot1.regional <- gheatmap(my.beast.tree.input, TPA.rawseq.year.cuttoff.p, color=NULL,width=track.width, offset=initial.track.offset+offset.dist,colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
    scale_fill_manual(name="Year", values=PHE.sublintest.year.cols$year.cols,breaks=PHE.sublintest.year.cols$year, guide = guide_legend(order = 1, ncol=2)) +
    ggnewscale::new_scale_fill()
  
  # Add country
  TPA.beast.subtree.test.global.plot1.regional <- gheatmap(TPA.beast.subtree.test.global.plot1.regional, TPA.rawseq.countries.p, color=NULL,width=track.width, offset=initial.track.offset+(offset.dist*2),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) + 
    scale_fill_manual(name="Country", values=continental.country.cols.brew2$country.col, breaks=continental.country.cols.brew2$Geo_Country, guide = guide_legend(order = 2)) +
    ggnewscale::new_scale_fill()
  # UK or non-UK
  TPA.beast.subtree.test.global.plot1.regional <- gheatmap(TPA.beast.subtree.test.global.plot1.regional,
                                                           TPA.rawseq.UK.p, color=NULL,width=track.width,offset=initial.track.offset+(offset.dist*3), colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) + 
    scale_fill_manual(name="England/Other", breaks=c("England","Other"), values=c("black","grey95"), na.value = "white", guide = guide_legend(order = 3, ncol=2)) +
    ggnewscale::new_scale_fill()
  # UK PHE region
  TPA.beast.subtree.test.global.plot1.regional <- gheatmap(TPA.beast.subtree.test.global.plot1.regional, sublin.test.region.meta, color=NULL,width=track.width, offset=initial.track.offset+(offset.dist*4),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) + 
    scale_fill_manual(name="UKHSA Region", values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region, na.value = "white", guide = guide_legend(order = 4)) +
    ggnewscale::new_scale_fill()
  
  # TPA sublineage
  #TPA.beast.subtree.test.global.plot1.regional <- gheatmap(TPA.beast.subtree.test.global.plot1.regional, data.frame(row.names=TPA.meta2.1$Sample_Name, Sublineage=TPA.meta2.1$TPA.pinecone.sublineage, stringsAsFactors = F), color=NULL,width=track.width,offset=initial.track.offset+(offset.dist*5), colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=2.5) + 
  #scale_fill_manual(name="Sublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage, guide = guide_legend(order = 5)) 
  
  TPA.beast.subtree.test.global.plot1.regional <- TPA.beast.subtree.test.global.plot1.regional + theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
    new_scale_fill() +
    geom_rootedge(2) +
    NULL
  
  # calculate number of taxa
  test.taxacount <- length(my.taxa.list)
  # Adjust final plot x and y axis to make space for labels using taxa counts
  x.axis.limits <- ggplot_build(TPA.beast.subtree.test.global.plot1.regional)$layout$panel_scales_x[[1]]$range$range
  TPA.beast.subtree.test.global.plot1.regional <- TPA.beast.subtree.test.global.plot1.regional + 
    coord_cartesian(y=c(-0.5-(test.taxacount/15),test.taxacount+2), x=c(x.axis.limits[1],x.axis.limits[2]+3))
  
  return(TPA.beast.subtree.test.global.plot1.regional)
}
################################################################################################


Great, now let’s plot a full beast tree

# function for x-axis time breaks needs tweaking for the full tree
TPA.Global.full.BeastTree.ukmeta <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(my.beast.tree = full.beast2.tree, my.metadata = TPA.meta2.1, my.phe.meta = PHE.metadata.linked, mrsd.fulltree = "2019-06-01") + scale_x_continuous(breaks=seq(1400,2020,50), minor_breaks=seq(1950, 2020, 5)), my.metadata = TPA.meta2.1, my.phe.meta = PHE.metadata.linked, track.scaling = 5)
Scale for 'x' is already present. Adding another scale for 'x', which will
replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
TPA.Global.full.BeastTree.ukmeta


#ggsave(paste0(Figure_output_directory,"SupFig7_TPA_FullBeastTree.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=185, height=240, device='pdf', dpi=1200)


Now do sublineage plots
Make some plots

# Sublineage 1
sublineage.1.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 1), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, track.scaling = 1.2)
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
# Sublineage.2
sublineage.2.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 2), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, track.scaling = 1)
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
# Sublineage.8
sublineage.8.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 8), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, track.scaling = 1.1)
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
# Sublineage.14
sublineage.14.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 14), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, track.scaling = 1.1)
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.


Plot together?
Maybe with sublineage 1 expanded?

p.beast.trees.heatmap.sublineages.combi.offset1 <- plot_grid(sublineage.2.tree.heatmap, 
          sublineage.8.tree.heatmap, 
          sublineage.14.tree.heatmap, 
          ncol=2, labels=c("B - Sublineage 2","C - Sublineage 8","D - Sublineage 14"), label_size=panel.lab.size, scale=0.95, vjust=1.0)

p.beast.trees.heatmap.sublineages.combi.offset2 <- plot_grid(sublineage.1.tree.heatmap, p.beast.trees.heatmap.sublineages.combi.offset1, labels=c("A - Sublineage 1", ""), label_size=panel.lab.size, scale=0.975, ncol=2, rel_widths=c(6,11), vjust=2.5)


p.beast.trees.heatmap.sublineages.combi.offset2

#ggsave(paste0(Figure_output_directory,"SupFig8_TPA-PHE_Sublineage-BeastTrees.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=265, height=230, device='pdf', dpi=1200)


Need to explore sublineage 14 a bit more to get dates for those subclades

sublineage.14.tree.heatmap + geom_tiplab(size=theme.text.size.within, linesize=0.4) #3


# Ok, there are multiple subclades in this tree
sublineage.14.tree.heatmap.data <- sublineage.14.tree.heatmap$data

# getMRCA(full.beast2.tree@phylo,c("PHE150150A","NL14","TPA_BCC122","TPA_BCC126","PHE140076A","TPA_UKBRG008"))  982
# full.beast2.tree@phylo$tip.label[phangorn::Descendants(full.beast2.tree@phylo, 982, type = c("tips"))[[1]]]

sublineage.14.lowerclade.list <- c("NL17", "NL19", "PHE140085A", "PHE140089A", "PHE150118A", "PHE150121A", "PHE150133A", "PHE150143A", "PHE150145A", "PHE150162A", "PHE150166A", "PHE150168A", "PHE160224A", "PHE160243A", "PHE160255A", "PHE160276A", "PHE160290A", "PHE160302A", "PHE160306A", "PHE170333A", "PHE170349A", "PHE170374A", "PHE170381A", "PHE170664A", "TPA_ESBCN005", "TPA_UKBIR032")

sublineage.14.upperclade.list <- c("NL14", "PHE140076A", "PHE150149A", "PHE150150A", "PHE150170A", "PHE160196A", "PHE160263A", "PHE160274A", "PHE160287A", "PHE160294A", "PHE160316A", "PHE160317A", "PHE170372A", "PHE170386A", "PHE170397A", "PHE170405A", "TPA_BCC081", "TPA_BCC088", "TPA_BCC089", "TPA_BCC101", "TPA_BCC122", "TPA_BCC126", "TPA_BCC136", "TPA_BCC169", "TPA_HUN180004", "TPA_HUN190020", "TPA_UKBIR044", "TPA_UKBRG007", "TPA_UKBRG008")

# Get MRCA date for lower clade
sublineage.14.lowerclade.list.tmrca <- sublineage.14.tree.heatmap.data[sublineage.14.tree.heatmap.data$node==getMRCA(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 14)@phylo, sublineage.14.lowerclade.list),"x"]

paste0("TMRCA for sublineage 14 lower clade: ",sublineage.14.lowerclade.list.tmrca)
[1] "TMRCA for sublineage 14 lower clade: 2006.53850498154"
# Get MRCA date for upper clade
sublineage.14.upperclade.list.tmrca <- sublineage.14.tree.heatmap.data[sublineage.14.tree.heatmap.data$node==getMRCA(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 14)@phylo, sublineage.14.upperclade.list),"x"]

paste0("TMRCA for sublineage 14 upper clade: ",sublineage.14.upperclade.list.tmrca)
[1] "TMRCA for sublineage 14 upper clade: 1999.15025243934"



Extract key information for sublineage 6 (two samples)

sublineage.6.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 6), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked)
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
sublineage.6.tree.heatmap.data <- sublineage.6.tree.heatmap$data

# Get MRCA date for upper clade
sublineage.6.beasttree.tmrca <- as.numeric(sublineage.6.tree.heatmap.data[sublineage.6.tree.heatmap.data$node==getMRCA(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 6)@phylo, c("PHE130048A", "PHE160283A")),"branch"])


paste0("TMRCA for sublineage 6 upper clade: ",sublineage.6.beasttree.tmrca)
[1] "TMRCA for sublineage 6 upper clade: 1982.61865062176"



### Extract sample & population statistics from datasets for use in manuscript text
Dataset and Geographical distributions

# dataset counts
paste0("Total UK samples in cleaned/deduplicated dataset: ",nrow(PHE.metadata.linked))
[1] "Total UK samples in cleaned/deduplicated dataset: 237"
paste0("Of which: ",nrow(PHE.metadata.linked[PHE.metadata.linked$is.PHE=="PHE",])," from PHE Ref lab at Colindale")
[1] "Of which: 195 from PHE Ref lab at Colindale"
paste0("Of which: ",nrow(PHE.metadata.linked[PHE.metadata.linked$is.PHE=="Other",])," from other labs")
[1] "Of which: 42 from other labs"
# proportion with geographical data
paste0("From UK samples, ", nrow(PHE.metadata.linked[(PHE.metadata.linked$phe_centre %notin% c("Not Known","UK (not England)")),])," were grouped into one of the 9 PH regions")
[1] "From UK samples, 217 were grouped into one of the 9 PH regions"
paste0("From UK samples, ", nrow(PHE.metadata.linked[PHE.metadata.linked$phe_centre=="UK (not England)",]), " were referred from outside England")
[1] "From UK samples, 2 were referred from outside England"
paste0("From UK samples, ", nrow(PHE.metadata.linked[PHE.metadata.linked$phe_centre=="Not Known",]), " had unknown region")
[1] "From UK samples, 18 had unknown region"
# counts & fractions by PHE region
PHE.geo.count
NA


Gender Orientation stats

PHE.orientation.counts
PHE.geo.orientation.counts
PHE.geo.HIV.counts
PHE.sublineage.orientation.counts
PHE.sublineage.Age


Sublineage Distributions

PHE.Lineage.count
PHE.sublin.count
PHE.geo.sublineage


Macrolide resistance stats

UK.macrolide.res <- PHE.metadata.linked %>%
  dplyr::group_by(A2058G, A2059G) %>%
  dplyr::summarise(Count.allele=n()) %>%
  dplyr::ungroup() %>%
  dplyr::mutate(total.count=sum(Count.allele), perc.allele=round((Count.allele/total.count)*100,1))
`summarise()` has grouped output by 'A2058G'. You can override using the `.groups` argument.
UK.macrolide.res

UK.macrolide.res.sublin <- PHE.metadata.linked %>%
  dplyr::group_by(TPA.pinecone.sublineage, A2058G, A2059G) %>%
  dplyr::summarise(Count.allele=n()) %>%
  dplyr::ungroup() %>%
  dplyr::group_by(TPA.pinecone.sublineage) %>%
  dplyr::mutate(total.count=sum(Count.allele), perc.allele=round((Count.allele/total.count)*100,1))
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'A2058G'. You can override using the `.groups` argument.
UK.macrolide.res.sublin


# Calculate long form df, with different 23S alleles (A2058G, A2059G, WT, Uncertain) v.s. sublineage
UK.macrolide.res.sublin.long <- PHE.metadata.linked %>%
  mutate(Resistance.allele=ifelse(A2058G=="Yes", "A2058G", ifelse(A2059G=="Yes", "A2059G", ifelse((A2058G=="No" & A2059G=="No"),"Wild Type", "Uncertain")))) %>%
  dplyr::group_by(TPA.pinecone.sublineage, Resistance.allele) %>%
  dplyr::summarise(Count.per.sublin.Macrolides=n()) %>%
  dplyr::mutate(total.sublin=sum(Count.per.sublin.Macrolides), 
                fraction=Count.per.sublin.Macrolides/total.sublin) %>%
  #dplyr::ungroup() %>%
  dplyr::arrange((Resistance.allele), .by_group=T) %>%
  dplyr::mutate(cum_fract = cumsum(fraction)) %>%
  dplyr::mutate(cum_fract.mid = cum_fract-(fraction/2)) %>%
  dplyr::mutate(Resistance.allele = factor(Resistance.allele, levels=rev(c("A2058G", "A2059G", "Uncertain", "Wild Type"))))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Make plot of macrolide resistance by sublineages
p.sublin.Macrolides.hbarplot <- ggplot(UK.macrolide.res.sublin.long, aes(Count.per.sublin.Macrolides, y=TPA.pinecone.sublineage, fill=Resistance.allele)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  scale_fill_manual(name="Macrolide\nResistance\nAllele",values=c("indianred2", "steelblue1","grey55", "grey90"), breaks=c("A2058G", "A2059G", "Uncertain", "Wild Type")) +
  labs(y="TPA Sublineage", x="Proportion with Macrolide Resistance Allele") +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  guides(fill=guide_legend(ncol=2)) +
  geom_text(data=UK.macrolide.res.sublin.long, aes(cum_fract.mid, y=TPA.pinecone.sublineage,label=Count.per.sublin.Macrolides), size=theme.text.size.within, inherit.aes = F) +
  NULL

p.sublin.Macrolides.hbarplot



# Combine plot with sublineage count bars
p.sublin.Macrolides.hbarplot.combi <- plot_grid(p.sublineage.hbarplot + guides(fill=guide_legend(ncol=3)), p.sublin.Macrolides.hbarplot + y.theme.strip, nrow=1, align=T, labels=c("A", "B"), label_size=panel.lab.size)

p.sublin.Macrolides.hbarplot.combi


#ggsave(paste0(Figure_output_directory,"SupFig9_TPA-PHE_Sublin-Macrolide-Res.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=160, height=120, device='pdf', dpi=1200)



Pairwise SNP analysis


OK, want to investigate the different patterns observable for the North East of England (pale blue) in Sublineage 1
Multiple ways we can do this - including SNP distances (also multiple ways to do that)

###
#Use phylogenetic distance from the SNP scaled tree
TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist <- ape::cophenetic.phylo(TPA.pyjar.tree.subset.uk)
TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt <- data.frame(Taxa1=row.names(TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist), TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist, stringsAsFactors = F) %>% tidyr::gather(Taxa2, Distance.Phylo, -Taxa1)
# Taxa Comparisons label
TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt$Taxa_combination <- sapply(1:nrow(TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt), function (x) paste0(sort(c(as.character(TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt$Taxa1[x]),as.character(TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt$Taxa2[x]))),collapse="___"))
# Merge together
#TPA.WGS.alignment.data.dist.melt <- dplyr::left_join(TPA.WGS.alignment.data.dist.melt, TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt[,c("Taxa_combination","Distance.Phylo")], by="Taxa_combination")

TPA.WGS.alignment.data.dist.melt <- TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt


TPA.WGS.alignment.data.dist.melt <- unique(TPA.WGS.alignment.data.dist.melt)


Ok, now bring in some metadata and comparisons

# Bring in and merge metadata
PHE.meta.pairwise.t1 <- PHE.metadata.linked[,c("Sample_Name","year","phe_centre","london","gender_orientation","hivpos","age_group","ukborn","TPA.pinecone.sublineage", "TPA_Lineage","Geo_Country","is.UK","is.PHE", "Sample_Year","date.decimal")]

colnames(PHE.meta.pairwise.t1) <- paste0(colnames(PHE.meta.pairwise.t1),".t1")
colnames(PHE.meta.pairwise.t1)[1] <- "Taxa1"
PHE.meta.pairwise.t2 <- PHE.metadata.linked[,c("Sample_Name","year","phe_centre","london","gender_orientation","hivpos","age_group","ukborn","TPA.pinecone.sublineage", "TPA_Lineage","Geo_Country","is.UK","is.PHE", "Sample_Year","date.decimal")]
colnames(PHE.meta.pairwise.t2) <- paste0(colnames(PHE.meta.pairwise.t2),".t2")
colnames(PHE.meta.pairwise.t2)[1] <- "Taxa2"

PHE.alignment.data.dist.melt.meta <- plyr::join(TPA.WGS.alignment.data.dist.melt,PHE.meta.pairwise.t1, by="Taxa1", type="left") 
PHE.alignment.data.dist.melt.meta <- plyr::join(PHE.alignment.data.dist.melt.meta,PHE.meta.pairwise.t2, by="Taxa2", type="left")

# Exclude missing data (e.g. missing sublineage) - this will also remove non-UK samples, since full metadata is missing here
PHE.alignment.data.dist.melt.meta <- PHE.alignment.data.dist.melt.meta[!is.na(PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t1),]
PHE.alignment.data.dist.melt.meta <- PHE.alignment.data.dist.melt.meta[!is.na(PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t2),]


Define comparisons

# Same sample
PHE.alignment.data.dist.melt.meta$same.sample <- ifelse(PHE.alignment.data.dist.melt.meta$Taxa1==PHE.alignment.data.dist.melt.meta$Taxa2,"same", "different")

# Years between samples
PHE.alignment.data.dist.melt.meta$year.distance <- abs(as.numeric(PHE.alignment.data.dist.melt.meta$year.t1) - as.numeric(PHE.alignment.data.dist.melt.meta$year.t2))

PHE.alignment.data.dist.melt.meta$Sample_Year.distance <- abs(as.numeric(PHE.alignment.data.dist.melt.meta$Sample_Year.t1) - as.numeric(PHE.alignment.data.dist.melt.meta$Sample_Year.t2))

# Years between decimal date (more precise temporal distance)
PHE.alignment.data.dist.melt.meta$decimal.date.distance <- abs(as.numeric(PHE.alignment.data.dist.melt.meta$date.decimal.t1) - as.numeric(PHE.alignment.data.dist.melt.meta$date.decimal.t2))

# Epidemiological time between - catagorical
PHE.alignment.data.dist.melt.meta$epi.time.distance.cat <- ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<1/12,"month", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=3/12, "quarter", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=6/12, "half year", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=1, "1 year",ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=2, "2 years", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=3, "3 years", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=4, "4 years", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=5, "5 years",  ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=6, "6 years",">6 years")))))))))

PHE.alignment.data.dist.melt.meta$epi.time.distance.cat <- factor(PHE.alignment.data.dist.melt.meta$epi.time.distance.cat, levels=c("month", "quarter","half year","1 year", "2 years", "3 years", "4 years", "5 years", "6 years", ">6 years"))

PHE.alignment.data.dist.melt.meta$epi.time.distance.cat.years <- ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=1, "0", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=2, "1", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=3, "2", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=4, "3", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=5, "4",  ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=6, "5",">5"))))))


# Same country
PHE.alignment.data.dist.melt.meta$same.country <- ifelse(PHE.alignment.data.dist.melt.meta$Geo_Country.t1 == PHE.alignment.data.dist.melt.meta$Geo_Country.t2, "same", "different")

# Is UK
PHE.alignment.data.dist.melt.meta$both.uk <- ifelse(PHE.alignment.data.dist.melt.meta$is.UK.t1 == PHE.alignment.data.dist.melt.meta$is.UK.t2, "same", "different")

# Is PHE
PHE.alignment.data.dist.melt.meta$both.PHE <- ifelse(PHE.alignment.data.dist.melt.meta$is.PHE.t1 == PHE.alignment.data.dist.melt.meta$is.PHE.t2, "same", "different")

# Same TPA Lineage (cleaned up classifications)
PHE.alignment.data.dist.melt.meta$same.TPA.Lineage <- ifelse(PHE.alignment.data.dist.melt.meta$TPA_Lineage.t1==PHE.alignment.data.dist.melt.meta$TPA_Lineage.t2, "same", "different")
PHE.alignment.data.dist.melt.meta$same.TPA.Lineage <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function(x) ifelse((PHE.alignment.data.dist.melt.meta$TPA_Lineage.t1[x]=="0" | PHE.alignment.data.dist.melt.meta$TPA_Lineage.t2[x]=="0"),NA,PHE.alignment.data.dist.melt.meta$same.TPA.Lineage[x]))

# Same TPA sublineage
PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster <- ifelse(PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t1==PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t2,"same", "different")
PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function(x) ifelse(((PHE.alignment.data.dist.melt.meta$same.sample[x]=="different" & PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t1[x]=="Singleton") |(PHE.alignment.data.dist.melt.meta$same.sample[x]=="different" & PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t2[x]=="Singleton")),"different",PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster[x]))

# Define Genetic relationships hierarchically
PHE.alignment.data.dist.melt.meta$genomic.cluster.hierarchy <- ifelse(PHE.alignment.data.dist.melt.meta$Distance==0,"Zero_SNPs", ifelse(PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster=="same","Same Sublineage", ifelse(PHE.alignment.data.dist.melt.meta$same.TPA.Lineage=="same", "Same Lineage","Different Lineage")))

PHE.alignment.data.dist.melt.meta$genomic.cluster.hierarchy.ph <- ifelse(PHE.alignment.data.dist.melt.meta$Distance.Phylo==0,"Zero_SNPs", ifelse(PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster=="same","Same Sublineage", ifelse(PHE.alignment.data.dist.melt.meta$same.TPA.Lineage=="same", "Same Lineage","Different Lineage")))


# Same PHE region
PHE.alignment.data.dist.melt.meta$same.PHE.region <- ifelse(PHE.alignment.data.dist.melt.meta$phe_centre.t1==PHE.alignment.data.dist.melt.meta$phe_centre.t2, "same", "different")
PHE.alignment.data.dist.melt.meta$PHE.centre.combination <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function (x) paste0(sort(c(as.character(PHE.alignment.data.dist.melt.meta$phe_centre.t1[x]),as.character(PHE.alignment.data.dist.melt.meta$phe_centre.t2[x]))),collapse="___"))

# does the combination included London?
PHE.alignment.data.dist.melt.meta$involves.London <- ifelse(PHE.alignment.data.dist.melt.meta$phe_centre.t1=="London" | PHE.alignment.data.dist.melt.meta$phe_centre.t2=="London", "London", "not-London")


# Orientation pair
PHE.alignment.data.dist.melt.meta$Orientation_combination <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function (x) paste0(sort(c(as.character(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]),as.character(PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]))),collapse="___"))

#PHE.alignment.data.dist.melt.meta$Orientation.Class <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function (x) ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="MSM" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="MSM", "MSM",
#       ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="MSM" | PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="MSM", "Mixed", 
#              ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="MSW" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="WSM","Heterosexual", 
#                     ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="WSM" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="MSW","Heterosexual","Unknown")))))

PHE.alignment.data.dist.melt.meta$Orientation.Class <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function (x) ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="GBMSM" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="GBMSM", "GBMSM",
                                                                                                                             ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x] %in% c("MSW","WSM") & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x] %in% c("MSW","WSM"),"Heterosexual",
                                                                                                                                    ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="GBMSM" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x] %in% c("MSW","WSM"), "Mixed", 
                                                                                                                                           ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x] %in% c("MSW","WSM") & PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="GBMSM", "Mixed", "Unknown")))))
                    


# Country Comparisons label
PHE.alignment.data.dist.melt.meta$Country_combinations <- paste0(PHE.alignment.data.dist.melt.meta$Geo_Country.t1,"___",PHE.alignment.data.dist.melt.meta$Geo_Country.t2)

# Subset to PHE data only (effectively already done, but let's be explicit)
PHE.TPA.alignment.data.dist.melt.meta <- PHE.alignment.data.dist.melt.meta[(PHE.alignment.data.dist.melt.meta$both.uk=="same" &  PHE.alignment.data.dist.melt.meta$both.PHE=="same"),]
PHE.TPA.alignment.data.dist.melt.meta <- PHE.TPA.alignment.data.dist.melt.meta[PHE.TPA.alignment.data.dist.melt.meta$PHE.only=="PHE",]

PHE.TPA.alignment.data.dist.melt.meta <- PHE.alignment.data.dist.melt.meta[(PHE.alignment.data.dist.melt.meta$both.uk=="same"),]



# Make single sided
PHE.TPA.alignment.data.dist.melt.meta <- PHE.TPA.alignment.data.dist.melt.meta[!duplicated(PHE.TPA.alignment.data.dist.melt.meta$Taxa_combination),]



### Perform a more detailed analysis of samples from the North East of England
Do a more detailed exploration of the North East of England

PHE.metadata.linked2.region_NorthEast <- PHE.metadata.linked[PHE.metadata.linked$phe_centre=="North East",]

# Constrain by samples being from the North East
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta[(PHE.alignment.data.dist.melt.meta$phe_centre.t1=="North East" & PHE.alignment.data.dist.melt.meta$same.sample=="different"),]

# Constrain by the same PHE region
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[PHE.alignment.data.dist.melt.meta.NorthEast.clusters$same.PHE.region=="same",]

#Just plot these distros
p.NorthEast.Pairwise.SNPs.unconstrained <- ggplot(PHE.alignment.data.dist.melt.meta.NorthEast.clusters, aes(Distance.Phylo)) + 
  geom_histogram(binwidth = 1) +
  theme_bw() +
  theme.text.size +
  labs(x="Pairwise SNP Distance", y="Comparison Count")

p.NorthEast.Pairwise.SNPs.unconstrained


Make a single linkage network from the North East samples


# Constrain by SNP distance (looser than previously - we just want to find basic groupings within sublineage 1 for NE samples)
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[PHE.alignment.data.dist.melt.meta.NorthEast.clusters$Distance.Phylo<=2,]

# And make sure that we actually have genetic distance data for all samples within the network
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[!is.na(PHE.alignment.data.dist.melt.meta.NorthEast.clusters$Distance.Phylo),]

# cleanup some data noise
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[!is.na(PHE.alignment.data.dist.melt.meta.NorthEast.clusters$year.t1),]

# prepare intput data (with edge info)
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1 <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[,c("Taxa1","Taxa2","Distance.Phylo","decimal.date.distance","year.distance","Orientation.Class","epi.time.distance.cat")]

############
# some issues with update to R4 - double sided matrix
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$edgename <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1), function(x) paste0(sort(as.character(unlist(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1[x,c("Taxa1","Taxa2")]))),collapse="___"))
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1 <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1[!duplicated(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$edgename),]

# Also having an issue with taxa as factors here
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Taxa1 <- as.character(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Taxa1)
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Taxa2 <- as.character(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Taxa2)
############

#inverse weight
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Distance.inv <- 1/PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Distance.Phylo

# Make actual network
set.seed(1235)
PHE.NorthEast.network <- network(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1, matrix.type = "edgelist", ignore.eval = FALSE, directed = F)

PHE.NorthEast.network.gg <- ggnetwork(PHE.NorthEast.network, layout = "kamadakawai", weights = "Distance.inv")
PHE.NorthEast.network.gg$Taxa1 <- PHE.NorthEast.network.gg$vertex.names

# extract temporal clusters from network
PHE.NorthEast.network.ig <- asIgraph(PHE.NorthEast.network)
PHE.NorthEast.network.components <- data.frame(Taxa1=network.vertex.names(PHE.NorthEast.network), vertex.no=as.vector(V(PHE.NorthEast.network.ig)), cluster=igraph::components(PHE.NorthEast.network.ig)$membership)
# For ease of story telling in the paper, flip clusters 2 and 3 around (so we can talk about 2 first)
PHE.NorthEast.network.components <- PHE.NorthEast.network.components %>%
  dplyr::mutate(cluster.old=cluster, cluster=ifelse(cluster.old==2, 3, ifelse(cluster.old==3,2,cluster.old)))
PHE.NorthEast.network.components$Cluster <- paste0("Cluster",PHE.NorthEast.network.components$cluster)

# merge metadata back in
PHE.NorthEast.network.gg <- plyr::join(PHE.NorthEast.network.gg, data.frame(Taxa1=PHE.metadata.linked$Sample_Name, PHE.metadata.linked[,c("phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], stringsAsFactors = F),by="Taxa1", type="left")

PHE.NorthEast.network.gg <- plyr::join(PHE.NorthEast.network.gg, data.frame(Taxa1=PHE.NorthEast.network.components$Taxa1, Cluster=PHE.NorthEast.network.components$Cluster), by="Taxa1", type="left")


Plot network

# Plot network
p.PHE.NorthEast.network.2SNP <- ggplot(PHE.NorthEast.network.gg, aes(x = x, y = y, xend = xend, yend = yend)) + 
  geom_edges(alpha=0.90, curvature = 0.2, aes(color=factor(Distance.Phylo), linetype=factor(Distance.Phylo))) +
  scale_color_manual(values=c("grey5","grey55","grey85"), name="SNP\nDistance") +
  scale_linetype(name="SNP\nDistance") +
  theme_blank() +
  ggnewscale::new_scale_color() + ggnewscale::new_scale("size") +
  geom_nodelabel(aes(color=gender_orientation, label=paste(Taxa1,year,sep="\n"),fontface = "bold"), alpha=0.8, size=theme.text.size.within-0.4, label.size=0.15, label.padding = unit(0.05, "lines")) +
  geom_nodes(size=1.0, aes(color=gender_orientation)) + 
  scale_color_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) + 
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
  NULL
p.PHE.NorthEast.network.2SNP


Ok, so three networks. Clear differentiation of a heterosexual network (with 0-snp distances) and two predominantly MSM networks
Let’s look at the phylogenetic context of those North East clusters we’ve defined. Pull out subtrees (from sublineage 1 subtree)

# Cluster 1
Beast.tree.NE.cluster1 <- getMRCA(full.beast2.tree@phylo, PHE.NorthEast.network.components[PHE.NorthEast.network.components$Cluster=="Cluster1","Taxa1"])
Beast.tree.NE.cluster1.subtree <- tree_subset(full.beast2.tree, node=Beast.tree.NE.cluster1, levels_back=0)

p.Beast.tree.NE.cluster1.subtree <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Beast.tree.NE.cluster1.subtree, TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, initial.track.offset = 10)
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
# Can't fit in tip labs, but since this is a polyphyletic subtree, it would be helpful to add a track to highlight the NE strains
PHE.metadata.linked$is.NorthEast <- ifelse(PHE.metadata.linked$phe_centre=="North East","North East", "Other England")
p.Beast.tree.NE.cluster1.subtree.cluster.highlight <- gheatmap(p.Beast.tree.NE.cluster1.subtree, data.frame(row.names=PHE.metadata.linked$Sample_Name, `North East`=PHE.metadata.linked$is.NorthEast), color=NULL,width=(1/max(p.Beast.tree.NE.cluster1.subtree$data$height)*3), offset=10+(4*5),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) + 
    scale_fill_manual(name="North East\nEngland", values=c("#A6CEE3","grey95"), breaks=c("North East","Other England"), na.value = "white", guide = guide_legend(order = 5)) +
    ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
# Just confirm the ClusterIDs for this subtree (make sure it doesn't enclose other clusters)
p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID <- gheatmap(p.Beast.tree.NE.cluster1.subtree.cluster.highlight, data.frame(row.names=PHE.NorthEast.network.components$Taxa1, ClusterID=PHE.NorthEast.network.components$Cluster), color=NULL,width=(1/max(p.Beast.tree.NE.cluster1.subtree$data$height)*3), offset=10+(4*6),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) + 
    scale_fill_manual(name="North East\nCluster", values=c("#7fc97f","#beaed4","#fdc086"), breaks=c("Cluster1","Cluster2","Cluster3"), na.value = "white", guide = guide_legend(order = 6)) +
    ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
# add a bit more room to the x axis
p.Beast.tree.NE.cluster1.subtree.cluster.highlight.x.axis.limits <- ggplot_build(p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID)$layout$panel_scales_x[[1]]$range$range
p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID <- p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID + 
    coord_cartesian(x=c(p.Beast.tree.NE.cluster1.subtree.cluster.highlight.x.axis.limits[1],p.Beast.tree.NE.cluster1.subtree.cluster.highlight.x.axis.limits[2]+4), y=c(-0.5-(length(unique(p.Beast.tree.NE.cluster1.subtree.cluster.highlight$data$label))/15),length(unique(p.Beast.tree.NE.cluster1.subtree.cluster.highlight$data$label))+2)) +
  theme(legend.margin = margin(-0.5,0,0,0, unit="mm"))
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
#p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID

#######################
# Cluster 2
Beast.tree.NE.cluster2 <- getMRCA(full.beast2.tree@phylo, PHE.NorthEast.network.components[PHE.NorthEast.network.components$Cluster=="Cluster2","Taxa1"])
Beast.tree.NE.cluster2.subtree <- tree_subset(full.beast2.tree, node=Beast.tree.NE.cluster2, levels_back=1)

p.Beast.tree.NE.cluster2.subtree <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Beast.tree.NE.cluster2.subtree, TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, initial.track.offset = 20) + geom_tiplab(size=theme.text.size.within, align=T, offset=5, linesize=0.4)
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
# Just add ClusterIDs for this subtree to highlight
p.Beast.tree.NE.cluster2.subtree <- gheatmap(p.Beast.tree.NE.cluster2.subtree, data.frame(row.names=PHE.NorthEast.network.components$Taxa1, ClusterID=PHE.NorthEast.network.components$Cluster), color=NULL,width=(1/max(p.Beast.tree.NE.cluster2.subtree$data$height)*3), offset=20+(4*5),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) + 
    scale_fill_manual(name="North East\nCluster", values=c("#7fc97f","#beaed4","#fdc086"), breaks=c("Cluster1","Cluster2","Cluster3"), na.value = "white", guide = guide_legend(order = 5, ncol=2)) +
    ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
# add a bit more room to the x axis
p.Beast.tree.NE.cluster2.subtree.x.axis.limits <- ggplot_build(p.Beast.tree.NE.cluster2.subtree)$layout$panel_scales_x[[1]]$range$range
p.Beast.tree.NE.cluster2.subtree <- p.Beast.tree.NE.cluster2.subtree + 
    coord_cartesian(x=c(p.Beast.tree.NE.cluster2.subtree.x.axis.limits[1],p.Beast.tree.NE.cluster2.subtree.x.axis.limits[2]+12), y=c(-0.5-(length(unique(p.Beast.tree.NE.cluster2.subtree$data$label))/20)-1,length(unique(p.Beast.tree.NE.cluster2.subtree$data$label))+0.5)) + 
  theme(legend.margin = margin(-0.5,0,0,0, unit="mm"))
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
#p.Beast.tree.NE.cluster2.subtree

############################
# Cluster 3
Beast.tree.NE.cluster3 <- getMRCA(full.beast2.tree@phylo, PHE.NorthEast.network.components[PHE.NorthEast.network.components$Cluster=="Cluster3","Taxa1"])
Beast.tree.NE.cluster3.subtree <- tree_subset(full.beast2.tree, node=Beast.tree.NE.cluster3, levels_back=1)

p.Beast.tree.NE.cluster3.subtree <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Beast.tree.NE.cluster3.subtree, TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, initial.track.offset = 26) + geom_tiplab(size=theme.text.size.within, align=T, offset=3, linesize=0.4)
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
# Just add ClusterIDs for this subtree to highlight
p.Beast.tree.NE.cluster3.subtree <- gheatmap(p.Beast.tree.NE.cluster3.subtree, data.frame(row.names=PHE.NorthEast.network.components$Taxa1, ClusterID=PHE.NorthEast.network.components$Cluster), color=NULL,width=(1/max(p.Beast.tree.NE.cluster3.subtree$data$height)*3), offset=26+(4*5),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) + 
    scale_fill_manual(name="North East\nCluster", values=c("#7fc97f","#beaed4","#fdc086"), breaks=c("Cluster1","Cluster2","Cluster3"), na.value = "white", guide = guide_legend(order = 5, ncol=2)) +
    ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
# add a bit more room to the x axis
p.Beast.tree.NE.cluster3.subtree.x.axis.limits <- ggplot_build(p.Beast.tree.NE.cluster3.subtree)$layout$panel_scales_x[[1]]$range$range
p.Beast.tree.NE.cluster3.subtree <- p.Beast.tree.NE.cluster3.subtree + 
    coord_cartesian(x=c(p.Beast.tree.NE.cluster3.subtree.x.axis.limits[1],p.Beast.tree.NE.cluster3.subtree.x.axis.limits[2]+12), y=c(-0.5-(length(unique(p.Beast.tree.NE.cluster3.subtree$data$label))/20)-1,length(unique(p.Beast.tree.NE.cluster3.subtree$data$label))+0.5)) + 
  theme(legend.margin = margin(-0.5,0,0,0, unit="mm"))
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
#p.Beast.tree.NE.cluster3.subtree

#p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID
#p.Beast.tree.NE.cluster2.subtree 
#p.Beast.tree.NE.cluster3.subtree 


Since Cluster 1 is really quite polyphyletic, it maybe more useful to show the clusters in context for that one

# Add North East identifier column
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight <- gheatmap(sublineage.1.tree.heatmap, data.frame(row.names=PHE.metadata.linked$Sample_Name, `North East`=PHE.metadata.linked$is.NorthEast), color=NULL,width=(1/max(sublineage.1.tree.heatmap$data$height)*3)*1.2, offset=0+(4*5)*1.2,colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) + 
    scale_fill_manual(name="North East\nEngland", values=c("#A6CEE3","grey95"), breaks=c("North East","Other England"), na.value = "white", guide = guide_legend(order = 5)) +
    ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
# Just confirm the ClusterIDs for this subtree (make sure it doesn't enclose other clusters)
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight <- gheatmap(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight, data.frame(row.names=PHE.NorthEast.network.components$Taxa1, ClusterID=PHE.NorthEast.network.components$Cluster), color=NULL,width=(1/max(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight$data$height)*3)*1.2, offset=0+(4*6)*1.2,colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) + 
    scale_fill_manual(name="North East\nCluster", values=c("#7fc97f","#beaed4","#fdc086"), breaks=c("Cluster1","Cluster2","Cluster3"), na.value = "white", guide = guide_legend(order = 6, ncol=2)) +
    ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
# add a bit more room to the x axis
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight.x.axis.limits <- ggplot_build(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight)$layout$panel_scales_x[[1]]$range$range
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight <- p.Beast.tree.sublineage1.NE.subtree.cluster.highlight + 
    coord_cartesian(x=c(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight.x.axis.limits[1],p.Beast.tree.sublineage1.NE.subtree.cluster.highlight.x.axis.limits[2]+4), y=c(-0.5-(length(unique(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight$data$label))/15),length(unique(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight$data$label))+2))
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
# reduce spacing between legend scales
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight <- p.Beast.tree.sublineage1.NE.subtree.cluster.highlight + theme(legend.margin = margin(-0.95,0,0,0, unit="mm"))
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight

  Plot together


p.Beast.tree.NE.subtrees.combi1 <- plot_grid(p.Beast.tree.NE.cluster2.subtree, p.Beast.tree.NE.cluster3.subtree, ncol=1, labels=c("C - Cluster 2", "D - Cluster 3"), vjust=1.0, label_size=panel.lab.size, scale=0.95)

p.Beast.tree.NE.subtrees.combi2 <- plot_grid(p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID, p.Beast.tree.NE.subtrees.combi1, ncol=2, rel_widths=c(3,2), labels=c("B - Cluster 1", ""), label_size=panel.lab.size)
p.Beast.tree.NE.subtrees.combi2



p.Beast.tree.NE.subtrees.combi3 <- plot_grid(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight, p.Beast.tree.NE.subtrees.combi1, ncol=2, rel_widths=c(8,7), labels=c("B - Sublineage 1 (All)", ""), label_size=panel.lab.size, scale=0.95, vjust=1.0)

p.Beast.tree.NE.subtrees.combi3



Look more closely at population demographics of these clusters

# Metadata on NE cluster 2
PHE.metadata.linked %>% 
  dplyr::filter(Sample_Name %in% Beast.tree.NE.cluster2.subtree@phylo$tip.label) %>%
  dplyr::group_by(Geo_Country, is.NorthEast, gender_orientation) %>%
  dplyr::summarise(Count=n())
`summarise()` has grouped output by 'Geo_Country', 'is.NorthEast'. You can override using the `.groups` argument.
# Metadata on NE cluster 3
PHE.metadata.linked %>% 
  dplyr::filter(Sample_Name %in% Beast.tree.NE.cluster3.subtree@phylo$tip.label) %>%
  dplyr::group_by(Geo_Country, is.NorthEast, gender_orientation) %>%
  dplyr::summarise(Count=n())
`summarise()` has grouped output by 'Geo_Country', 'is.NorthEast'. You can override using the `.groups` argument.
# Country info on NE cluster 3
TPA.meta2.1 %>% 
  dplyr::filter(Sample_Name %in% Beast.tree.NE.cluster3.subtree@phylo$tip.label) %>%
  dplyr::group_by(Geo_Country) %>%
  dplyr::summarise(Count=n())

# Separate metadata records show Hungarian sample "TPA_HUN180001" came from a male bisexual (MSWM).


Examine SNP scaled tree for distances


# Extract information about SNP distances
TPA.NEcluster3.pyjartree.mrca <- getMRCA(TPA.pyjar.tree, as.character(unlist(TPA.meta2.1[TPA.meta2.1$Sample_Name %in% Beast.tree.NE.cluster3.subtree@phylo$tip.label,"Sample_Name"])))


TPA.NEcluster3.pyjartree.subtree <- tree_subset(TPA.pyjar.tree, node=TPA.NEcluster3.pyjartree.mrca, levels_back=1)

ggtree(TPA.NEcluster3.pyjartree.subtree) + geom_tiplab(size=theme.text.size.within)

ggtree(TPA.NEcluster3.pyjartree.subtree)$data



Do some analysis of nearest neighbour and distances to MRCAs

calculate.years.from.mrca <- function(current.ggtree.phylo, current.ggtree.data){
  #current.ggtree <- Beast.tree.NE.cluster3.subtree
  all.tips <- current.ggtree.phylo$tip.label
  dist.2.mrca <- NULL
  ### put dates into df
  current.ggtree.data$mrca.median <- 2019.5 - current.ggtree.data$height_median
  current.ggtree.data$year <- as.numeric(round(2019.5 - current.ggtree.data$height_median,3))
  current.ggtree.data$mrca.95high <- round(2019.5 - sapply(1:nrow(current.ggtree.data),function(x) as.numeric(unlist(current.ggtree.data[x,"height_0.95_HPD"]))[1]), 3)
  current.ggtree.data$mrca.95low <- round(2019.5 - sapply(1:nrow(current.ggtree.data),function(x) as.numeric(unlist(current.ggtree.data[x,"height_0.95_HPD"]))[2]), 3)
  # extract dates between sample and its MRCA using loop
  for (current.node in all.tips) {
    current.parent <- c(match(current.node,current.ggtree.phylo$tip.label), phangorn::Ancestors(current.ggtree.phylo, match(c(current.node), current.ggtree.phylo$tip.label), "parent"))
    
    current.nodelist <- current.ggtree.data[current.ggtree.data$node %in% current.parent,]
    current.dist.2.mrca <- c(current.node, as.numeric(current.nodelist[1,"year"]-current.nodelist[2,"year"]))
    dist.2.mrca <- rbind(dist.2.mrca, current.dist.2.mrca)
  }
  dist.2.mrca <- data.frame(Sample_Name=as.character(dist.2.mrca[,1]), dist.to.mrca=as.numeric(dist.2.mrca[,2]), stringsAsFactors=F)
  return(dist.2.mrca)
}

### All samples in global tree
dist.mrca.all.TPA <- calculate.years.from.mrca(full.beast2.tree@phylo, full.beast2.tree@data)


Merge dist2MRCA with metadata

PHE.metadata.linked.dist2mrca <- left_join(PHE.metadata.linked, dist.mrca.all.TPA, by="Sample_Name")

p.time2mrca.orientation <- ggplot(PHE.metadata.linked.dist2mrca, aes(gender_orientation, dist.to.mrca, color=gender_orientation)) + 
  geom_quasirandom(size=0.75, alpha=0.5) +
  theme_light() + theme.text.size +
  coord_flip() +
  labs(x="Gender Orientation", y="Years to MRCA", color="Gender Orientation") +
  theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
  scale_color_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation)

p.time2mrca.phe_region <- ggplot(PHE.metadata.linked.dist2mrca, aes(phe_centre, dist.to.mrca, color=phe_centre)) + 
  geom_quasirandom(size=0.75, alpha=0.5) +
  theme_light() + theme.text.size +
  coord_flip(ylim=c(0,40)) +
  labs(x="UKHSA Region", y="Years to MRCA", color="UKHSA Region") +
  theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
  scale_color_manual(name="UKHSA\nRegion", values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region)

p.time2mrca.phe_region.orientation <- ggplot(PHE.metadata.linked.dist2mrca, aes(phe_centre, dist.to.mrca, color=gender_orientation)) + 
  geom_quasirandom(size=0.75, alpha=0.5) +
  theme_light() + theme.text.size +
  coord_flip(ylim=c(0,20)) +
  labs(x="UKHSA Region", y="Years to MRCA") +
  theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
  scale_color_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation)
p.time2mrca.phe_region.orientation
Warning: Removed 15 rows containing missing values (position_quasirandom).

p.time2mrca.sublineage <- ggplot(PHE.metadata.linked.dist2mrca, aes(TPA.pinecone.sublineage, dist.to.mrca, color=TPA.pinecone.sublineage)) + 
  geom_quasirandom(size=0.75, alpha=0.5) +
  theme_light() + theme.text.size +
  coord_flip() +
  labs(x="TPA Lineage", y="Years to MRCA", color="TPA Lineage") +
  theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
  scale_color_manual(values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage)
p.time2mrca.sublineage
Warning: Removed 15 rows containing missing values (position_quasirandom).

p.time2mrca.Lineage <- ggplot(PHE.metadata.linked.dist2mrca, aes(TPA_Lineage, dist.to.mrca, color=TPA_Lineage)) + 
  geom_quasirandom(size=0.75, alpha=0.5) +
  theme_light() + theme.text.size +
  coord_flip() +
  labs(x="TPA Lineage", y="Years to MRCA (Median of Posterior)", color="TPA Lineage") +
  theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
  scale_color_manual(values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage)



Maybe can make an MST of the North East samples for grapetree?

TPA.pyjar.tree.subset.NorthEast <- ape::keep.tip(TPA.pyjar.tree, as.character(unlist(PHE.metadata.linked[PHE.metadata.linked$phe_centre=="North East","Sample_Name"])))

#ggtree(TPA.pyjar.tree.subset.NorthEast)
#write.tree(TPA.pyjar.tree.subset.NorthEast, paste0(Data_input_directory,"TPA.UK-only-NorthEast.pyjar.2022-02-26.tre"))

# Write out a metadata sheet for the relevant information
PHE.metadata.linked.grapetree <- PHE.metadata.linked[,c("Sample_Name", "year","gender_orientation","phe_centre","hivpos","ukborn","TPA_Lineage","TPA.pinecone.sublineage")]
colnames(PHE.metadata.linked.grapetree)[1] <- "ID"

#write.table(PHE.metadata.linked.grapetree, paste0(Data_input_directory,"TPA.UK-only.grapetree.meta.2022-02-03.tsv"), sep = "\t", quote=F, row.names = F)

Alternative approach using MST instead of networks for North East data

# Read in MST
#TPA.NorthEastEngland.Grapetree.file <- paste0(Data_input_directory,"TPA-UK-NorthEast-2022-02-26.GenderOrientation-MSTree.inkscaped.+node-counts+GBMSM.svg")

p.TPA.NorthEastEngland.Grapetree <- ggdraw() + draw_image(TPA.NorthEastEngland.Grapetree.file)
p.TPA.NorthEastEngland.Grapetree


p.TPA.NorthEastEngland.Grapetree.header <- plot_grid(p.TPA.NorthEastEngland.Grapetree, labels=c("A - Network Clusters (North East England)"), label_size=panel.lab.size, scale=0.95)


Plot with beast trees

#p.PHE.NorthEast_MST.with.beast.subtrees.combi <- plot_grid(p.TPA.NorthEastEngland.Grapetree, p.Beast.tree.NE.subtrees.combi3, ncol=1, rel_heights=c(3,6), labels=c("A - Network Clusters (North East England)", ""), label_size=panel.lab.size, scale = 0.95)

p.PHE.NorthEast_MST.with.beast.subtrees.combi <- plot_grid(p.TPA.NorthEastEngland.Grapetree.header, p.Beast.tree.NE.subtrees.combi3, ncol=1, rel_heights=c(3,7))



p.PHE.NorthEast_MST.with.beast.subtrees.combi

#ggsave(paste0(Figure_output_directory,"Fig3_Sublin1.NorthEast.MST+Beast.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=200, height=245, device='pdf', dpi=1200)


Do some analysis of major sublineages over time by region - could this influence observations about sublineages?

# Generate some stats by PHE Region
PHE.major.sublineage.PHEcentre.date <- PHE.metadata.linked %>% 
  dplyr::filter(TPA.pinecone.sublineage %in% c(1,14)) %>%
  dplyr::group_by(TPA.pinecone.sublineage, phe_centre, year) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.sublin=sum(Count)) %>%
  dplyr::arrange(desc(phe_centre), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'phe_centre'. You can override using the `.groups` argument.
ggplot(PHE.major.sublineage.PHEcentre.date, aes(year, phe_centre, size=Count, color=TPA.pinecone.sublineage)) +
  geom_point() + 
  facet_grid(.~TPA.pinecone.sublineage) +
  theme_light() +
  theme.text.size +
  scale_color_manual(values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage)



p.PHE.major.sublineage.PHEcentre.date.bubbleplot <- ggplot(PHE.major.sublineage.PHEcentre.date, aes(year, TPA.pinecone.sublineage, color=TPA.pinecone.sublineage)) +
  geom_point(alpha=0.65, aes(size=Count)) + 
  geom_line(alpha=0.25) +
  facet_grid(factor(gsub("\\ ","\n",phe_centre), levels=gsub("\\ ","\n",PHE.region.cols.brew$UKHSA.region))~., switch='y') +
  theme_light() +
  theme(strip.placement = "outside") +
  theme(strip.background = element_rect(color='white', fill='white',linetype="solid"), strip.text.y=element_text(color = "grey25",angle=0, size=5)) + 
  scale_size_area(max_size = 4.5,breaks=c(1,5,10,20,30,40)) +
  theme.text.size +
  scale_color_manual(values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage) + 
  labs(y="Region", x="Year", color="Sublineage") 
 
p.PHE.major.sublineage.PHEcentre.date.bubbleplot
geom_path: Each group consists of only one observation. Do you need to adjust
the group aesthetic?


Do some specific analysis for the 3 Northern regions

# Generate some stats by PHE Region
 PHE.metadata.linked %>% 
  dplyr::filter(phe_centre %in% c("North East", "North West", "Yorkshire and Humber")) %>%
  dplyr::summarise(count=n())

 PHE.metadata.linked %>% 
  dplyr::filter(phe_centre %in% c("North East", "North West", "Yorkshire and Humber")) %>%
  dplyr::group_by(year) %>%
  dplyr::summarise(count=n())


p.PHE.major.sublineage.3NorthernRegions <- PHE.metadata.linked %>% 
  dplyr::filter(phe_centre %in% c("North East", "North West", "Yorkshire and Humber")) %>%
  dplyr::group_by(TPA.pinecone.sublineage, year, phe_centre) %>%
  dplyr::summarise(Count=n()) %>%
  ggplot(aes(year, Count, fill=phe_centre)) + 
  geom_bar(stat='identity', width=0.65) +
  scale_fill_manual(values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region) +
  theme_bw() + theme.text.size +
  scale_x_continuous(breaks=seq(2012,2018,1)) +
  scale_y_continuous(breaks=pretty) +
  labs(title="Samples in 3 Northern Regions", x="Collection Year", y="Sample Count", fill="Public Health\nRegion") +
  theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
  #geom_text(aes(x=year,y=Count-0.5, label=Count), color='grey95', size=theme.text.size.within) +
  NULL
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'year'. You can override using the `.groups` argument.
p.PHE.major.sublineage.3NorthernRegions


Single linkage network of identical genomes from UK

# Constrain by SNP distance (identical in the asr snp tree)
PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta[PHE.alignment.data.dist.melt.meta$Distance.Phylo==0,]

# and a max of 2 years
#PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta.identicals[PHE.alignment.data.dist.melt.meta.identicals$decimal.date.distance<=2,]


# And make sure that we actually have genetic distance data for all samples within the network
PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta.identicals[!is.na(PHE.alignment.data.dist.melt.meta.identicals$Distance.Phylo),]

# remove self-samples
PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta.identicals[PHE.alignment.data.dist.melt.meta.identicals$same.sample=="different",]


# cleanup some data noise
PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta.identicals[!is.na(PHE.alignment.data.dist.melt.meta.identicals$year.t1),]

# prepare intput data (with edge info)
PHE.alignment.data.dist.melt.meta.identicals.input1 <- PHE.alignment.data.dist.melt.meta.identicals[,c("Taxa1","Taxa2","Distance.Phylo","decimal.date.distance","year.distance","Orientation.Class","epi.time.distance.cat.years","epi.time.distance.cat")]

############
# some issues with update to R4 - double sided matrix
PHE.alignment.data.dist.melt.meta.identicals.input1$edgename <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta.identicals.input1), function(x) paste0(sort(as.character(unlist(PHE.alignment.data.dist.melt.meta.identicals.input1[x,c("Taxa1","Taxa2")]))),collapse="___"))
PHE.alignment.data.dist.melt.meta.identicals.input1 <- PHE.alignment.data.dist.melt.meta.identicals.input1[!duplicated(PHE.alignment.data.dist.melt.meta.identicals.input1$edgename),]

# Also having an issue with taxa as factors here
PHE.alignment.data.dist.melt.meta.identicals.input1$Taxa1 <- as.character(PHE.alignment.data.dist.melt.meta.identicals.input1$Taxa1)
PHE.alignment.data.dist.melt.meta.identicals.input1$Taxa2 <- as.character(PHE.alignment.data.dist.melt.meta.identicals.input1$Taxa2)
############
# Deduplicate

#inverse weight
PHE.alignment.data.dist.melt.meta.identicals.input1$decimal.date.distance.inv <- 1/1/(PHE.alignment.data.dist.melt.meta.identicals.input1$decimal.date.distance+0.04)

# Make actual network
set.seed(1236)
PHE.identicals.network <- network(PHE.alignment.data.dist.melt.meta.identicals.input1, matrix.type = "edgelist", ignore.eval = FALSE, directed = F, loops = F)

#PHE.identicals.network.gg <- ggnetwork(PHE.identicals.network, layout = "kamadakawai", weights = "decimal.date.distance.inv")
#PHE.identicals.network.gg <- ggnetwork(PHE.identicals.network, layout = "fruchtermanreingold", weights = "decimal.date.distance")
PHE.identicals.network.gg <- ggnetwork(PHE.identicals.network, layout = "fruchtermanreingold")

PHE.identicals.network.gg$Taxa1 <- PHE.identicals.network.gg$vertex.names

# extract temporal clusters from network
PHE.identicals.network.ig <- asIgraph(PHE.identicals.network)
PHE.identicals.network.components <- data.frame(Taxa1=network.vertex.names(PHE.identicals.network), vertex.no=as.vector(V(PHE.identicals.network.ig)), cluster=igraph::components(PHE.identicals.network.ig)$membership)
PHE.identicals.network.components$Cluster <- paste0("Cluster",PHE.identicals.network.components$cluster)

# merge metadata back in
PHE.identicals.network.gg <- plyr::join(PHE.identicals.network.gg, data.frame(Taxa1=PHE.metadata.linked$Sample_Name, PHE.metadata.linked[,c("phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], stringsAsFactors = F),by="Taxa1", type="left")

PHE.identicals.network.gg <- plyr::join(PHE.identicals.network.gg, data.frame(Taxa1=PHE.identicals.network.components$Taxa1, Cluster=PHE.identicals.network.components$Cluster), by="Taxa1", type="left")


# 
# Add temporal colour scale
#unique(PHE.identicals.network.gg$epi.time.distance.cat)

epi.time.distance.cat.cols <- rev(colorRampPalette(brewer.pal(8, "Greys"))(length(unique(PHE.identicals.network.gg$epi.time.distance.cat))-1))


# Plot network
p.PHE.identicals.network.0SNP <- ggplot(PHE.identicals.network.gg, aes(x = x, y = y, xend = xend, yend = yend)) + 
  geom_edges(alpha=0.90, curvature = 0.2, aes(color=factor(epi.time.distance.cat), linetype=factor(epi.time.distance.cat))) +
  #scale_color_manual(values=c("grey5","grey35","grey55", "grey65", "grey75"), name="SNP\nDistance") +
  scale_color_manual(name="Temporal\nDistance", values = epi.time.distance.cat.cols) +
  scale_linetype(name="Temporal\nDistance") +
  theme_blank() +
  ggnewscale::new_scale_color() + ggnewscale::new_scale("size") +
  #geom_nodelabel(aes(color=gender_orientation, label=paste(Taxa1,year,sep="\n"),fontface = "bold"), alpha=0.8, size=theme.text.size.within-0.4, label.size=0.15, label.padding = unit(0.05, "lines")) +
  geom_nodes(size=2.5, aes(color=gender_orientation), alpha=0.9) + 
  scale_color_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) + 
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
  NULL
p.PHE.identicals.network.0SNP

Plot this against a UK tree?

gheatmap(ggtree(TPA.pyjar.tree.subset.uk),
data.frame(row.names=PHE.identicals.network.components$Taxa1, Cluster=PHE.identicals.network.components$Cluster))


Some stats from this

p.PHE.identical.Orientation_class.bydatedist <- PHE.alignment.data.dist.melt.meta %>%
  dplyr::filter(same.sample=="different", Distance.Phylo==0) %>%
  #filter(decimal.date.distance<=1) %>%
  dplyr::group_by(epi.time.distance.cat, Orientation.Class) %>% 
  dplyr::summarise(Count.class.date=n()) %>%
  dplyr::mutate(sum.class=sum(Count.class.date), fract.class=Count.class.date/sum.class) %>%
  ggplot(aes(x=epi.time.distance.cat, y=Count.class.date, fill=Orientation.Class)) +
  geom_bar(stat='identity', position='stack') +
  theme_bw() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
  labs(x="Time between samples", y="Interaction Count", fill="Orientation Type")
`summarise()` has grouped output by 'epi.time.distance.cat'. You can override using the `.groups` argument.
p.PHE.identical.Orientation_class.bydatedist



  
p.PHE.identical.Orientation_class.byZerodist.cluster <- PHE.identicals.network.gg %>%
  dplyr::filter(!is.na(Orientation.Class)) %>%
  dplyr::group_by(Cluster, Orientation.Class) %>% 
  dplyr::summarise(Count.class.cluster=n()) %>%
  dplyr::mutate(sum.class=sum(Count.class.cluster), fract.class=Count.class.cluster/sum.class) %>%
  dplyr::arrange(desc(sum.class)) %>%
  dplyr::ungroup() %>%
  dplyr::mutate(Cluster=as_factor(Cluster)) %>%
  ggplot(aes(x=Cluster, y=Count.class.cluster, fill=Orientation.Class)) +
  geom_bar(stat='identity', position='stack') + 
  theme_bw() +
  x.theme.axis.rotate +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
  labs(x="Identical Genome Cluster", y="Interaction Count", fill="Orientation Type")
`summarise()` has grouped output by 'Cluster'. You can override using the `.groups` argument.
p.PHE.identical.Orientation_class.byZerodist.cluster


d.PHE.identical.GenderOrientation.byZerodist.cluster <- left_join(PHE.identicals.network.components[,c("Taxa1","Cluster")], PHE.metadata.linked[,c("Sample_Name","phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], by=c("Taxa1"="Sample_Name")) %>%
  dplyr::group_by(TPA.pinecone.sublineage, Cluster, gender_orientation) %>%
  dplyr::summarise(count.orient.cluster=n()) %>%
  dplyr::mutate(count.cluster=sum(count.orient.cluster), fract=count.orient.cluster/count.cluster) %>%
  dplyr::ungroup() %>%
  dplyr::arrange(desc(count.cluster)) %>%
  dplyr::mutate(Cluster.o=as_factor(Cluster))
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'Cluster'. You can override using the `.groups` argument.
# Plot sample counts by genome cluster (coloured by orientation)
p.PHE.identical.GenderOrientation.byZerodist.cluster <- d.PHE.identical.GenderOrientation.byZerodist.cluster %>%
  ggplot(aes(Cluster.o, count.orient.cluster, fill=gender_orientation)) + 
  geom_bar(stat="identity", width=0.65) +
  scale_fill_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation, guide = guide_legend(order = 1)) +
  theme_light() +
  x.theme.axis.rotate + 
  scale_y_continuous(breaks=seq(0,45,5)) +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
  labs(x="Identical Genome Cluster", y="Sample Count", fill="Patient Gender Orientation") 

# Add details of sublineage  
p.PHE.identical.GenderOrientation.byZerodist.cluster <- p.PHE.identical.GenderOrientation.byZerodist.cluster + 
  ggnewscale::new_scale_color() +
  geom_point(data=(d.PHE.identical.GenderOrientation.byZerodist.cluster %>% select(Cluster.o, TPA.pinecone.sublineage) %>% distinct()), aes(Cluster.o, -1.5, color=TPA.pinecone.sublineage), inherit.aes = F) + scale_color_manual(values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage, name="Sublineage", guide = guide_legend(order = 2)) +
  NULL

# Add a sublineage axis label (bit of a hack)
p.PHE.identical.GenderOrientation.byZerodist.cluster <- p.PHE.identical.GenderOrientation.byZerodist.cluster + 
  geom_text(data=data.frame(lab="Sublineage", y=-1.5, x=28, stringsAsFactors=F), aes(label=lab, x=x, y=y), hjust = 0.1, size=theme.text.size.within, inherit.aes = F) +
  coord_cartesian(x=c(1, 27), clip='off')
  
p.PHE.identical.GenderOrientation.byZerodist.cluster


######gxxxxgsave(paste0(Figure_output_directory,"SupFig6_Identical-SNP-clust_orientation.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=120, height=100, device='pdf', dpi=1200)


Possible to introduce some more info into that plot?

d.PHE.identical.region.byZerodist.cluster <- left_join(PHE.identicals.network.components[,c("Taxa1","Cluster")], PHE.metadata.linked[,c("Sample_Name","phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], by=c("Taxa1"="Sample_Name")) %>%
  dplyr::group_by(TPA.pinecone.sublineage, Cluster, phe_centre) %>%
  dplyr::summarise(count.region.cluster=n()) %>%
  dplyr::mutate(count.cluster=sum(count.region.cluster), fract=count.region.cluster/count.cluster) %>%
  dplyr::ungroup() %>%
  dplyr::arrange(desc(count.cluster)) %>%
  dplyr::mutate(Cluster.o=as_factor(Cluster))
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'Cluster'. You can override using the `.groups` argument.
p.PHE.identical.Region.byZerodist.cluster <- d.PHE.identical.region.byZerodist.cluster %>%
  ggplot(aes(Cluster.o, count.region.cluster, fill=phe_centre)) + 
  geom_bar(stat="identity", width=0.65, position='fill') +
  scale_fill_manual(name="UKHSA\nRegion", values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region, guide = guide_legend(order = 1)) +
  theme_light() +
  x.theme.axis.rotate + 
  scale_y_continuous(breaks=seq(0,45,5)) +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
  guides(fill=guide_legend(ncol=2)) +
  labs(x="Identical Genome Cluster", y="Region Proportion", fill="UKHSA Region") 
p.PHE.identical.byZerodist.cluster.barcombi <- plot_grid(p.PHE.identical.GenderOrientation.byZerodist.cluster + x.theme.strip, p.PHE.identical.Region.byZerodist.cluster, ncol=1, axis="rlt", align=T, rel_heights = c(2,1), labels=c("B","C"), label_size=panel.lab.size)

#p.PHE.identical.byZerodist.cluster.barcombi
#p.PHE.identicals.network.0SNP

plot_grid(p.PHE.identicals.network.0SNP, p.PHE.identical.byZerodist.cluster.barcombi, ncol=1, rel_heights=c(2,3), labels=c("A",""), label_size=panel.lab.size)



p.PHE.identical.byZerodist.cluster.barcombi.noNet <- plot_grid(p.PHE.identical.GenderOrientation.byZerodist.cluster + x.theme.strip, p.PHE.identical.Region.byZerodist.cluster, ncol=1, axis="rlt", align=T, rel_heights = c(2,1), labels=c("A","B"), label_size=panel.lab.size)
p.PHE.identical.byZerodist.cluster.barcombi.noNet 



#ggsave(paste0(Figure_output_directory,"SupFig6_Identical-SNP-clust_orientation.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=120, height=120, device='pdf', dpi=1200)
PHE.identicals.network.gg.region.scatterpie.groups <- PHE.identicals.network.gg %>%
  dplyr::select(Cluster, Taxa1, phe_centre) %>%
  dplyr::distinct() %>%
  dplyr::group_by(Cluster, phe_centre) %>% 
  dplyr::summarise(Count.centre=n()) %>%
  dplyr::mutate(x=Cluster, y=3.5) %>%
  pivot_wider(names_from="phe_centre", values_from="Count.centre", values_fill=0) %>%
  dplyr::select(Cluster,x,y,unique(PHE.identicals.network.gg$phe_centre)) %>%
  dplyr::ungroup() %>%
  dplyr::mutate(Cluster.numeric=as.numeric(1:27))
`summarise()` has grouped output by 'Cluster'. You can override using the `.groups` argument.
  

p.PHE.identical.GenderOrientation.byZerodist.cluster + 
  ggnewscale::new_scale_fill() #+

NA
NA


Get a few more stats on the largest cluster (Cluster 8)

#d.PHE.identical.GenderOrientation.byZerodist.cluster %>% filter(Cluster=="Cluster8")

PHE.identicals.network.gg.identical.cluster8 <- PHE.identicals.network.gg %>% filter(Cluster=="Cluster8")  %>%
  select(vertex.names, Orientation.Class, phe_centre, year, TPA_Lineage, TPA.pinecone.sublineage, hivpos, Cluster)

sort(unique(PHE.identicals.network.gg.identical.cluster8$year))
[1] 2012 2013 2014 2015 2016 2017 2018


Get some more information about the heterosexual only clusters

PHE.identicals.network.gg.identical_heteroclusters <- PHE.identicals.network.gg %>% filter(Cluster %in% c("Cluster12", "Cluster20", "Cluster27"))  %>%
  select(vertex.names, Cluster, gender_orientation, phe_centre, year, TPA_Lineage, TPA.pinecone.sublineage, hivpos) %>% 
  distinct() %>%
  arrange(Cluster, year, gender_orientation)

PHE.identicals.network.gg.identical_heteroclusters

  And do the same for the small mixed/GBMSM clusters

PHE.identicals.network.gg.identical_not.heteroclusters <- PHE.identicals.network.gg %>% filter(Cluster %notin% c("Cluster12", "Cluster20", "Cluster27", "Cluster8"))  %>%
  select(vertex.names, Cluster, gender_orientation, phe_centre, year, TPA_Lineage, TPA.pinecone.sublineage, hivpos) %>% 
  distinct() %>%
  arrange(Cluster, year, gender_orientation)
PHE.identicals.network.gg.identical_not.heteroclusters

What proportion of heterosexuals have an identical GBMSM paired genome?


# Delineate heterosexual clusters
d.PHE.identical.heterosexual.clusters <- d.PHE.identical.GenderOrientation.byZerodist.cluster %>% 
  dplyr::mutate(is.heterosexual=ifelse(gender_orientation%in% c("MSW", "WSM"), "heterosexual", ifelse(gender_orientation=="GBMSM","GBMSM", "Unknown"))) %>%
  dplyr::group_by(Cluster,is.heterosexual) %>% 
  dplyr::mutate(count.hetero=sum(count.orient.cluster), fract.hetero=sum(count.orient.cluster)/count.cluster) %>%
  dplyr::ungroup() %>%
  dplyr::filter(is.heterosexual=="heterosexual") %>% 
  dplyr::select(-c(count.orient.cluster, gender_orientation, fract)) %>%
  dplyr::distinct() %>%
  dplyr::mutate(cluster.type=ifelse(fract.hetero==1, "hetero.only", "other"))

d.PHE.identical.heterosexual.clusters 

# What proportion of heterosexuals (n=20) are in a heterosexual-only cluster?
d.PHE.identical.heterosexual.clusters %>% 
  dplyr::group_by(cluster.type) %>%
  dplyr::summarise(count.in.hetero.cluster=sum(count.hetero)) %>% 
  dplyr::mutate(fract.in.hetero=count.in.hetero.cluster/sum(count.in.hetero.cluster))
  

#left_join(PHE.identicals.network.components[,c("Taxa1","Cluster")], PHE.metadata.linked[,c("Sample_Name","phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], by=c("Taxa1"="Sample_Name"))


Revisions 03-2023 onwards


Look at proportion of genomes at different coverage thresholds

# Cumulative proportion of N counts in genomes
PHE.metadata.Ncount.cummulative.UK <- PHE.metadata.linked %>% 
  dplyr::filter(is.UK=="UK") %>%
  dplyr::group_by(`Proportion-N_>5_mapping+masking_Nichols`) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.Count=sum(Count)) %>%
  dplyr::mutate(fraction=Count/total.Count, cum_fract=cumsum(fraction), cum_count=cumsum(Count)) %>%
  dplyr::mutate(Dataset="UK (n=237)")
PHE.metadata.Ncount.cummulative.UK


PHE.metadata.Ncount.cummulative.ALL <- TPA.meta2.1 %>% 
  dplyr::filter(full.temporal.analysis=="Yes") %>%
  dplyr::group_by(`Proportion-N_>5_mapping+masking_Nichols`) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.Count=sum(Count)) %>%
  dplyr::mutate(fraction=Count/total.Count, cum_fract=cumsum(fraction), cum_count=cumsum(Count)) %>%
  dplyr::mutate(Dataset="All (n=520)") 
PHE.metadata.Ncount.cummulative.ALL
PHE.metadata.Ncount.cummulative.combi <- rbind(PHE.metadata.Ncount.cummulative.UK, PHE.metadata.Ncount.cummulative.ALL)




p.cumulative.Ncount.for.datset <-  ggplot(PHE.metadata.Ncount.cummulative.combi , aes(`Proportion-N_>5_mapping+masking_Nichols`, cum_fract, group=Dataset, color=Dataset)) + 
  geom_point(alpha=0.75, size=1) +
  theme_light() + 
  theme.text.size + theme(legend.position = 'top') +
  labs(y="Cumulative fraction of genomes", x="Proportion of sites masked to N") +
  scale_y_continuous(breaks=seq(0,1,0.1))

p.cumulative.Ncount.for.datset


BEAST 95% HPD calculations (provide more details for 520 dataset )

BEAST.median <- 1.28e-7
BEAST.95HPD <- c(1.07e-7, 1.48e-7)
SS14.aln.length <- 1139569


1/(BEAST.median * SS14.aln.length)
[1] 6.855662
1/(BEAST.95HPD * SS14.aln.length)
[1] 8.201166 5.929221



Further evaluation of sublineage 6 (reviewer response) using ancestral reconstruction performed on the global TPA-only alignment/tree used in Beale 2021.

TPA.treetime.ancestral.tree <- read.nexus(TPA.treetime.ancestral.tree.file)
TPA.treetime.ancestral.tree.data <- fortify(TPA.treetime.ancestral.tree)

ggtree(TPA.treetime.ancestral.tree) + geom_nodelab(size=2)


# Read in and process TPA-only vcf (to confirm sites are the same)
TPA.only.midpoint.treetime.ancestral.vcf <- read.vcfR(TPA.treetime.ancestral.vcf.file, verbose = FALSE)
TPA.only.midpoint.treetime.ancestral.vcf.fix <- getFIX(TPA.only.midpoint.treetime.ancestral.vcf)
TPA.only.midpoint.treetime.ancestral.vcf.fix <- data.frame(TPA.only.midpoint.treetime.ancestral.vcf.fix[,c(2,4,5)], stringsAsFactors = F)
TPA.only.midpoint.treetime.ancestral.vcf.fix$in.TPA.only <- "yes"
TPA.only.midpoint.treetime.ancestral.vcf.fix$Key <- 1:nrow(TPA.only.midpoint.treetime.ancestral.vcf.fix)


Extract genotype sites

TPA.treetime.ancestral.vcf.gt <- extract_gt_tidy(TPA.only.midpoint.treetime.ancestral.vcf)
Extracting gt element GT
TPA.treetime.ancestral.vcf.gt.f <- plyr::join(TPA.treetime.ancestral.vcf.gt, TPA.only.midpoint.treetime.ancestral.vcf.fix[,c("Key","POS")], by="Key", type="left")

TPA.treetime.ancestral.vcf.gt.f$POS <- as.numeric(TPA.treetime.ancestral.vcf.gt.f$POS)
TPA.treetime.ancestral.vcf.gt.f$gt_GT <- as.numeric(TPA.treetime.ancestral.vcf.gt.f$gt_GT)

TPA.treetime.ancestral.vcf.gt.f.spread <- tidyr::spread(TPA.treetime.ancestral.vcf.gt.f[,c("POS","Indiv","gt_GT")], POS, gt_GT) 

Use snpEff to annotate multi-vcf, and then pull in annotations here

TPA.snpEff <- read.table(TPA.snpEff.file,header = T, check.names = F, comment.char = "",sep="\t")

TPA.snpEff.filt <- TPA.snpEff[!(TPA.snpEff$`ANN[*].GENE`=="gene-TPASS_RS00040" & TPA.snpEff$`ANN[*].EFFECT`=="intragenic_variant"),]
TPA.snpEff.filt[TPA.snpEff.filt$`ANN[*].EFFECT`==".","ANN[*].EFFECT"] <- "intragenic_variant"


TPA.snpEff.filt %>% dplyr::group_by(`ANN[*].EFFECT`) %>% summarise(Count=n())
TPA.snpEff.filt %>% dplyr::group_by(`ANN[*].GENE`) %>% summarise(Count=n())
TPA.snpEff.filt %>% dplyr::group_by(`ANN[*].GENE`,`ANN[*].EFFECT`) %>% summarise(Count=n())
`summarise()` has grouped output by 'ANN[*].GENE'. You can override using the `.groups` argument.
TPA.snpEff.filt.var.per.pos <- TPA.snpEff.filt %>% dplyr::group_by(POS) %>% summarise(Count=n())
TPA.snpEff.filt.var.per.pos.multi <- as.numeric(as.character(unlist(TPA.snpEff.filt.var.per.pos[TPA.snpEff.filt.var.per.pos$Count>1,"POS"])))

TPA.snpEff.filt[TPA.snpEff.filt$POS %in% TPA.snpEff.filt.var.per.pos.multi,]
NA


Lets pull in gene function (where known) for these sites from the gff

SS14.gff <- ape::read.gff(SS14.gff.file)
SS14.gff.cds <- SS14.gff[SS14.gff$type=="CDS",]

#### function to extract different fields from attributes column
getAttributeField <- function (x, field, attrsep = ";") {
     s = strsplit(x, split = attrsep, fixed = TRUE)
     sapply(s, function(atts) {
         a = strsplit(atts, split = "=", fixed = TRUE)
         m = match(field, sapply(a, "[", 1))
         if (!is.na(m)) {
             rv = a[[m]][2]
         }
         else {
             rv = as.character(NA)
         }
         return(rv)
     })
}
###
#getAttributeField(SS14.gff.cds$attributes, "Name")


# Extract attribute elements from gff
SS14.gff.cds$geneid <- gsub("gene\\-","",getAttributeField(SS14.gff.cds$attributes, "Parent"))
SS14.gff.cds$locus_tag <- getAttributeField(SS14.gff.cds$attributes, "locus_tag")
SS14.gff.cds$gene <- getAttributeField(SS14.gff.cds$attributes, "gene")
SS14.gff.cds$product <- getAttributeField(SS14.gff.cds$attributes, "product")
SS14.gff.cds$proteinid <- getAttributeField(SS14.gff.cds$attributes, "protein_id")
# create a merged locus_tag/gene the way snpEff does
SS14.gff.cds$geneid <- sapply(1:nrow(SS14.gff.cds), function(x) ifelse(is.na(SS14.gff.cds$gene[x]),SS14.gff.cds$locus_tag[x], SS14.gff.cds$gene[x]))
SS14.gff.cds$gene.coords <- paste0(SS14.gff.cds$start,":",SS14.gff.cds$end)

SS14.gff.cds


# read in snp classifications, and apply to discriminatory SNPs
Write this as a function. Takes 4 arguments: - dataframe of snps for each sample in wide matrix format (e.g. TPA.treetime.ancestral.vcf.gt.f.spread) - longform list of SNPs and possible alleles (e.g. TPA.treetime.ancestral.vcf.fix) - variant annotations dataframe (e.g. TPA.snpEff.filt) - a vector of two nodes in the tree to compare (e.g. tt.nodes.to.compare.SS14)

extract_branch_site_allelic_functions <- function(allele.matrix.spread, snp.table, snp.annotation.table, nodes.list){
  # filter SNP matrix to only include the two nodes of interest
  discriminatory.sites1 <- allele.matrix.spread[allele.matrix.spread$Indiv %in% nodes.list,]
  discriminatory.sites2 <- tidyr::gather(discriminatory.sites1,POS,Gt,-Indiv) %>% 
  tidyr::spread(Indiv, Gt)
  # Filter SNPs under consideration to those that are different between the two nodes
  discriminatory.sites2 <- discriminatory.sites2[(discriminatory.sites2[,2]!=discriminatory.sites2[,3]),]
  discriminatory.sites2 <- discriminatory.sites2[order(as.numeric(discriminatory.sites2$POS)),]
  # merge in the details about alleles at each relevant SNP position
  discriminatory.sites2 <- plyr::join(discriminatory.sites2, snp.table,by=c('POS'), type='left')
  # deal with multi-allelic sites, and discriminate between them
  discriminatory.sites2$ALT.multi <- discriminatory.sites2$ALT
  discriminatory.sites2$ALT <- sapply(1:nrow(discriminatory.sites2), function(x) strsplit(discriminatory.sites2$ALT.multi[x],",")[[1]][sort(as.numeric(((discriminatory.sites2[x,c(2,3)]))))[2]])
  # merge in the annotation for the appropriate allele/SNPs
  discriminatory.sites2.snpeff <- plyr::join(snp.annotation.table[,c("POS","ALT","ANN[*].ALLELE","ANN[*].EFFECT","ANN[*].GENE","ANN[*].HGVS_C","ANN[*].HGVS_P")], discriminatory.sites2[,c("POS","REF","ALT",nodes.list)], type="right", by=c("POS","ALT"))
  discriminatory.sites2.snpeff[is.na(discriminatory.sites2.snpeff$`ANN[*].EFFECT`),"ANN[*].EFFECT"] <- "intragenic_variant"
  # return output
  return(discriminatory.sites2.snpeff)
}


#tt.nodes.to.compare.SS14.vs.Nichols.TPA <- c("NODE_0000005","NODE_0000103")

#tt.nodes.to.compare.sublineage6.vs.MRCA.TPA <- c("NODE_0000003","NODE_0000002")
tt.nodes.to.compare.sublineage6.vs.MRCA.TPA <- c("NODE_0000001","NODE_0000002")

sublin6.vs.mrca.Nichols.branch_site_alleles.TPA <- extract_branch_site_allelic_functions(TPA.treetime.ancestral.vcf.gt.f.spread,TPA.only.midpoint.treetime.ancestral.vcf.fix,TPA.snpEff.filt, tt.nodes.to.compare.sublineage6.vs.MRCA.TPA)

sublin6.vs.mrca.Nichols.branch_site_alleles.TPA %>% dplyr::group_by(`ANN[*].EFFECT`) %>% dplyr::summarise(count=n())

paste0("All Variants: ", nrow(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA))
[1] "All Variants: 51"
paste0("Unique Sites: ", length(unique(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA$POS)))
[1] "Unique Sites: 51"
paste0("Synonymous Variants: ", nrow(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA[sublin6.vs.mrca.Nichols.branch_site_alleles.TPA$`ANN[*].EFFECT`=="synonymous_variant",]))
[1] "Synonymous Variants: 11"
paste0("Non-Synonymous Variants: ", nrow(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA[sublin6.vs.mrca.Nichols.branch_site_alleles.TPA$`ANN[*].EFFECT`=="missense_variant",]))
[1] "Non-Synonymous Variants: 36"
paste0("Intragenic Variants :", nrow(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA[sublin6.vs.mrca.Nichols.branch_site_alleles.TPA$`ANN[*].EFFECT`=="intragenic_variant",]))
[1] "Intragenic Variants :4"
sublin6.vs.mrca.Nichols.branch_site_alleles.TPA$dist.from.last.var <- c(0, sapply(2:nrow(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA) , function(x) as.numeric(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA$POS[x]) - as.numeric(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA$POS[x-1]))) 

mean(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA$dist.from.last.var)
[1] 22075.57
median(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA$dist.from.last.var)
[1] 15611
min(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA$dist.from.last.var)
[1] 0
max(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA$dist.from.last.var)  
[1] 106334
p.sublineage6.ancestral.SNPs.genomepos <- ggplot(sublin6.vs.mrca.Nichols.branch_site_alleles.TPA, aes(x=as.numeric(POS), y=dist.from.last.var)) + 
  geom_point(size=1, alpha=0.5) +
  #geom_bar(stat='identity', alpha=0.5) +
  #geom_line(alpha=0.1) +
  theme_light() + theme(text = element_text(size = 10)) +
  coord_cartesian(xlim=c(0,SS14.aln.length)) +
  scale_x_continuous(breaks=pretty) +
  scale_y_log10() +
  labs(x="SS14 Genome Position (NC_021508.1; (bp))", y="Distance of variant from previous variant site (bp)", title="Genome position of SNPs delineating Sublineage 6 from MRCA node")
  
p.sublineage6.ancestral.SNPs.genomepos
Warning: Transformation introduced infinite values in continuous y-axis

p.sublineage6.ancestral.SNPs.dist.between.histo <- sublin6.vs.mrca.Nichols.branch_site_alleles.TPA %>%
  ggplot(aes(x=dist.from.last.var)) + 
  scale_x_log10() +
  geom_histogram(bins=50) + 
  theme_light() + theme(text = element_text(size = 10)) +
  labs(x="Distance of variant from previous variant site (bp)", y="Count") + coord_flip()

p.sublineage6.ancestral.SNPs.dist.between.histo
Warning: Transformation introduced infinite values in continuous x-axis
Warning: Removed 1 rows containing non-finite values (stat_bin).

plot_grid(p.sublineage6.ancestral.SNPs.genomepos, p.sublineage6.ancestral.SNPs.dist.between.histo + y.theme.strip , rel_widths = c(8,1), align = T)
Warning: Transformation introduced infinite values in continuous y-axis
Warning: Transformation introduced infinite values in continuous x-axis
Warning: Removed 1 rows containing non-finite values (stat_bin).



Do some further analysis of the North East sublineage distributions. We have 35 samples collected from these regions, of which 17 were collected from 2014 onwards. Is sublineage 14 missing by chance (could we be missing it simply because we haven’t collected enough samples) or is this more likely to reflect true uneven regional distributions?

# How many genomes found in Northern regions before and after first detection of sublineage 14 in 2014?
 PHE.metadata.linked %>%
  dplyr::mutate(before2014=ifelse(year>=2014,"2014onwards", "pre2014")) %>%
  dplyr::filter(phe_centre %in% c("North East", "North West", "Yorkshire and Humber")) %>%
  dplyr::group_by(before2014) %>%
  dplyr::summarise(count=n())

# What are the proportions of different sublineages around the UK before and after 2014?
PHE.meta.post2014.sublin.fracs <- PHE.metadata.linked %>% 
  #dplyr::filter(year>=2014) %>%
  dplyr::mutate(before2014=ifelse(year>=2014,"2014onwards", "pre2014")) %>%
  dplyr::group_by(before2014, TPA.pinecone.sublineage) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.all=sum(Count)) %>%
  dplyr::mutate(fraction=Count/total.all) %>%
  dplyr::arrange(desc(TPA.pinecone.sublineage), .by_group=T) %>%
  dplyr::mutate(cum_fract = cumsum(fraction)) %>%
  dplyr::mutate(cum_fract.mid = cum_fract-(fraction/2)) %>%
  dplyr::mutate(Lineage.perc=(Count/sum(Count)*100))
`summarise()` has grouped output by 'before2014'. You can override using the `.groups` argument.
PHE.meta.post2014.sublin.fracs 


# simulating poisson process r to work out how many samples we would expect in Northern England under poisson distribution

# What % of sublineage 14 samples are found in the total population?
post2014.sublin14.freq <- PHE.meta.post2014.sublin.fracs %>% filter(before2014=="2014onwards", TPA.pinecone.sublineage==14) %>% select(Lineage.perc) %>% pull()
Adding missing grouping variables: `before2014`
 

# Simulate and plot a Poisson distribution of how many sublineage 14 samples we would expect to find if we randomly selected 17 samples at 22% 
data.frame(rpois=rpois(1000000, 17/(100/post2014.sublin14.freq))) %>%
  ggplot(aes(rpois)) + geom_histogram(binwidth=1) +
  scale_x_continuous(breaks=seq(0,20,2)) +
  theme_light() +
  labs(x="Samples Found", y="Simulation Count")


# What are the quantile distributions from that?
quantile(rpois(1000000, 17/(100/post2014.sublin14.freq)), probs=c(0.01, 0.05, 0.5, 0.95, 0.99))
 1%  5% 50% 95% 99% 
  0   1   4   7   9 
median(rpois(1000000, 17/(100/post2014.sublin14.freq)))
[1] 4
mean(rpois(1000000, 17/(100/post2014.sublin14.freq)))
[1] 3.796368
# What is the probability of finding no samples (assuming uniform unbiased coverage)?
data.frame(n=seq(0,20,1), dpois=sapply(seq(0,20,1), function(x) dpois(x, lambda=17/(100/post2014.sublin14.freq)))) %>% 
  ggplot(aes(x=n, y=dpois)) + 
  geom_bar(stat='identity') +
  scale_x_continuous(breaks=pretty) +
  theme_light() +
  labs(x="Samples Found", y="Probability")


paste("Probability of finding zero samples is ", round(dpois(0, lambda=17/(100/post2014.sublin14.freq)), 5)) 
[1] "Probability of finding zero samples is  0.02244"
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayAtIFRyZXBvbmVtYSBVSyBVS0hTQS1jb2hvcnQgQW5hbHlzaXMgMjAyMi4gUmV2aXNpb24gMDQtMjAyMyIKb3V0cHV0OgogIHBkZl9kb2N1bWVudDogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICB3b3JkX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKTWFrZSBhIGNsZWFuIGVudmlyb25tZW50CmBgYHtyfQogcm0obGlzdD1scygpKQpgYGAKXApMb2FkIHBhY2thZ2VzCmBgYHtyfQpwYWNrYWdlcy5saXN0IDwtIGMoImdncGxvdDIiLCJ0cmVlaW8iLCJnZ3RyZWUiLCJnZ25ld3NjYWxlIiwiYXBlIiwiZHBseXIiLCJ0aWR5dmVyc2UiLCJ0aWR5ciIsInBoeXRvb2xzIiwiUkNvbG9yQnJld2VyIiwibHVicmlkYXRlIiwicmVhZHhsIiwiZ2dmb3JjZSIsImdnc3RhbmNlIiwiZ2dyaWRnZXMiLCJjb3dwbG90IiwiaGV4YmluIiwic2NhbGVzIiwiaGF2ZW4iLCJuZXR3b3JrIiwiZ2duZXR3b3JrIiwiaW50ZXJncmFwaCIsImlncmFwaCIsImdncmFwaCIsImdyYXBobGF5b3V0cyIsInNjYXR0ZXJwaWUiLCJtYXBzIiwibWFwZGF0YSIsIm1hcHRvb2xzIiwicmdkYWwiLCJyZ2VvcyIsImJyb29tIiwiZ2dyZXBlbCIsImdncmlkZ2VzIiwibWFnaWNrIiwiZ2diZWVzd2FybSIsImdncmFzdHIiLCAiZXh0cmFmb250Iiwic3ZnbGl0ZSIpCgojInBseXIiLCJDYWlybyIsImdnbWFwIiwiZW1vamlmb250IiwiclBpbmVjb25lIiwicGFpcnNucCIsIkNvb3JkaW5hdGVDbGVhbmVyIiwiZ3JpZEV4dHJhIiwiZGVuZGV4dGVuZCIsImdnZGVuZHJvIiwKCiNCaW9jTWFuYWdlcjo6aW5zdGFsbCgiZ2d0cmVlIikKI0Jpb2NNYW5hZ2VyOjppbnN0YWxsKCJ0cmVlaW8iKQoKZm9yKHBrZyBpbiBwYWNrYWdlcy5saXN0KXsKICBldmFsKGJxdW90ZShsaWJyYXJ5KC4ocGtnKSkpKSB9CmBgYApcCkNvbmZpcm0gY3VycmVudCBlbnZpcm9ubWVudGFsIHNldHVwCmBgYHtyfQpSLlZlcnNpb24oKQpwcmludChzZXNzaW9uSW5mbygpKQpgYGAKXApNYWtlIHNvbWUgc2hvcnRjdXRzIGZvciBwbG90dGluZyAKYGBge3J9CnkudGhlbWUuc3RyaXAgPC0gdGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcy55PSBlbGVtZW50X2JsYW5rKCkpCnkudGhlbWUuc3RyaXAucGFydGlhbCA8LSB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcy55PSBlbGVtZW50X2JsYW5rKCkpCgp4LnRoZW1lLnN0cmlwIDwtIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueD0gZWxlbWVudF9ibGFuaygpKQp4LnRoZW1lLnN0cmlwLnBhcnRpYWwgPC0gdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueD0gZWxlbWVudF9ibGFuaygpKQp4LnRoZW1lLnN0cmlwLmxhYnMgPC0gdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKQoKeC50aGVtZS5heGlzLnJvdGF0ZSA8LSB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpCgpsZWdlbmQuc3RyaXAgPC0gdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKdGhlbWUudGV4dC5zaXplIDwtIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkKCiclbm90aW4lJyA8LSBOZWdhdGUoJyVpbiUnKQoKbWF4LmZvbnQuc2l6ZSA8LSA3CmJhc2ljLmZvbnQuc2l6ZSA8LSA2Cm1pbi5mb250LnNpemUgPC0gNS4yNQp0aGVtZS50ZXh0LnNpemUgPC0gdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gYmFzaWMuZm9udC5zaXplKSkKdGhlbWUudGV4dC5zaXplLndpdGhpbiA8LSAoNS8xNCkqbWluLmZvbnQuc2l6ZQpwYW5lbC5sYWIuc2l6ZSA8LSAxMAoKYGBgClwKU3BlY2lmeSByYXcgZGF0YSAtIGdsb2JhbCBkYXRhc2V0CmBgYHtyfQojRGF0YV9pbnB1dF9kaXJlY3RvcnkgPC0gIi9Vc2Vycy9tYjI5L1BhcGVycy9UcmVwb25lbWFfVUstUEhFLWdlbi1lcGlfMjAyMS9EYXRhLyIKI0RhdGFfaW5wdXRfZGlyZWN0b3J5IDwtICIvVXNlcnMvbWIyOS9QYXBlcnMvVHJlcG9uZW1hX1VLLVBIRS1nZW4tZXBpXzIwMjEvUm5vdGVib29rL1Jub3RlYm9va18wOS0yMDIyL2RhdGEvIgpEYXRhX2lucHV0X2RpcmVjdG9yeSA8LSBwYXN0ZTAoZ2V0d2QoKSwgIi9pbnB1dGRhdGEvIikKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyMjIFRyZWUgZGF0YSAKCiMgTUwgdHJlZSAocmVmaW5lZCBkYXRhc2V0KQpUUEEuTUx0cmVlLmZpbGUgPC0gcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEtdWJlci5yZW1hc2tlZC4yMDIwLTExLTEwLmdvb2Rjb3YyNS5ndWJiaW5zLlNOUHMuYWxuLnJlbmFtZWQuZml4LXplcm8tZGlzdC50cmVlZmlsZSIpCgojIFB5amFyIHRyZWUgKHJlZmluZWQgZGF0YXNldCkKVFBBLnB5amFyLmZpbGUgPC0gcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEtdWJlci5yZW1hc2tlZC4yMDIwLTExLTEwLmdvb2Rjb3YyNS5ndWJiaW5zLlNOUHMuYWxuLnJlbmFtZWQucHlqYXIudHJlIikKCiMgRnVsbCBzaXplIEJFQVNUMiBhbmFseXNpcyAtIHByZXZpb3VzbHkgZ2VuZXJhdGVkIGFzIHBhcnQgb2YgQmVhbGUsIDIwMjEuCmZ1bGwuYmVhc3QyLnRyZWUuZmlsZSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS11YmVyX2JlYXN0Ml9zdHJpY3Qtc2t5bGluZS01MDBNXzEwcG9wX2NvbnNlbnN1cy50cmVlIikKCiMgQW5jZXN0cmFsIHJlY29uc3RydWN0aW9uIG9mIGdsb2JhbCBUUEEgTUwgdHJlZSBmcm9tIFRyZWVUaW1lIChyZWZpbmVkIGRhdGFzZXQpClRQQS50cmVldGltZS5hbmNlc3RyYWwudHJlZS5maWxlIDwtIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiVFBBLmFubm90YXRlZF90cmVlLmZpeC1odW5nLm5leHVzIikKVFBBLnRyZWV0aW1lLmFuY2VzdHJhbC52Y2YuZmlsZSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS11YmVyLm1pZHBvaW50LmFuY2VzdHJhbF9zZXF1ZW5jZXMuZml4LWh1bmcudmNmIikKIyBGdW5jdGlvbmFsbHkgYW5ub3RhdGVkIHZhcmlhbnRzLCBleHRyYWN0ZWQgZnJvbSBzbnBFZmYgdmNmIGludG8gdHN2IHVzaW5nIHNucFNpZnQKVFBBLnNucEVmZi5maWxlIDwtIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiVFBBLXViZXIubWlkcG9pbnQuYW5jZXN0cmFsX3NlcXVlbmNlcy5yZWxhYi5iY2YuYW5uLnZjZi52YXJ0YWIuc2VwbGluZS50c3YiKQojIEdmZiBmaWxlIGZvciBTUzE0IHJlZmVyZW5jZSBnZW5vbWUsIGNvbnRhaW5pbmcgZ2VuZSBwb3NpdGlvbnMvYW5ub3RhdGlvbnMKU1MxNC5nZmYuZmlsZSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRyZXBvbmVtYV9wYWxsaWR1bV9zdWJzLl9wYWxsaWR1bV9TUzE0Lk5DXzAyMTUwOC4xLjIwMjEtMDYtMTMuZ2ZmIikKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMjIyMgTWV0YSBkYXRhIAoKIyBTdXBwbGVtZW50IGZyb20gVFBBLVViZXIgcGFwZXIgLSBCZWFsZSwgMjAyMSAKVFBBLm1ldGEyLmZpbGUgPC0gcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJTdXBfRGF0YTFfR2xvYmFsX1NhbXBsZS1NZXRhZGF0YV9fMDktMjAyMi54bHN4IikKCiMgRW5nbGFuZCBzcGVjaWZpYyBtZXRhZGF0YSBjb2xsYXRlZCBieSBQSEUvVUtIU0EKUEhFLm1ldGFkYXRhLmxpbmtlZC5maWxlIDwtIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiU3VwX0RhdGEyX1RQQS5VSy1vbmx5LlBIRS5tZXRhZGF0YS4yMDIyLTAyLTAyLnhsc3giKQoKIyBFbmdsYW5kIHNwZWNpZmljIG1hcHBpbmcgc2hhcGVmaWxlIGRhdGEgd2l0aCBQdWJsaWMgSGVhbHRoIEJvdW5kYXJpZXMKIyBJbXBvcnRlZCBkYXRhZmlsZSBmcm9tIGh0dHBzOi8vZ2VvcG9ydGFsLnN0YXRpc3RpY3MuZ292LnVrL2RhdGFzZXRzL3B1YmxpYy1oZWFsdGgtZW5nbGFuZC1jZW50cmVzLWRlY2VtYmVyLTIwMTYtZnVsbC1jbGlwcGVkLWJvdW5kYXJpZXMtaW4tZW5nbGFuZC9leHBsb3JlP2xvY2F0aW9uPTUyLjk1MDAwMCUyQy0yLjAwMDAwMCUyQzYuODgKVUsucHVibGljaGVhbHRoLnNoYXBlZmlsZS5kYXRhIDwtIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiUHVibGljX0hlYWx0aF9FbmdsYW5kX0NlbnRyZXNfKERlY2VtYmVyXzIwMTYpX0JvdW5kYXJpZXMiKQoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMjIyMgRXh0ZXJuYWxseSBwbG90dGVkIGZpZ3VyZXMgKGUuZy4gR3JhcGVUcmVlKSBmb3IgaW5jbHVzaW9uIGluIG11bHRpcGFuZWwgZmlndXJlcwoKIyBFeHRlcm5hbGx5IHBsb3R0ZWQgZ3JhcGV0cmVlIG1pbmltdW0gc3Bhbm5pbmcgdHJlZSBmb3Igd2hvbGUgb2YgRW5nbGFuZCAtIGNvZGUgdG8gZXh0cmFjdCBzdWJ0cmVlIHRoYXQgd2FzIHVzZWQgdG8gbWFrZSB0aGlzIGlzIGluY2x1ZGVkIGxhdGVyIGluIHRoaXMgUm5vdGVib29rClRQQS5VSy5HcmFwZXRyZWUuc3VibGluZWFnZXMuZmlsZSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS1VSy0yMDIyLTAyLTAzLnN1YmxpbmVhZ2UtTVNUcmVlLklua3NjYXBlZC5zdmciKQoKIyBFeHRlcm5hbGx5IHBsb3R0ZWQgZ3JhcGV0cmVlIG1pbmltdW0gc3Bhbm5pbmcgdHJlZSBmb3Igd2hvbGUgb2YgRW5nbGFuZCAtIDMtdmFyaWFibGUgcGxvdHMKVFBBLlVLLkdyYXBldHJlZS4zd2F5LmZpbGUgPC0gcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEtVUstMjAyMi0wMi0xNi4tTVNUcmVlXzMtd2F5LWZpZ3VyZS5JbnNjYXBlZC0zLnN2ZyIpCgojIEV4dGVybmFsbHkgcGxvdHRlZCBncmFwZXRyZWUgbWluaW11bSBzcGFubmluZyB0cmVlIGZvciB3aG9sZSBvZiBFbmdsYW5kIC0gSElWIHN0YXR1cwpUUEEuVUsuR3JhcGV0cmVlLkhJVi5maWxlIDwtIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiVFBBLVVLLTIwMjItMDItMDMuSElWc3RhdHVzLU1TVHJlZV9pbmtzY2FwZWQuc3ZnIikKCiMgRXh0ZXJuYWxseSBwbG90dGVkIGdyYXBldHJlZSBtaW5pbXVtIHNwYW5uaW5nIHRyZWUgZm9yIE5vcnRoIEVhc3QgRW5nbGFuZCBuZXR3b3JrcwpUUEEuTm9ydGhFYXN0RW5nbGFuZC5HcmFwZXRyZWUuZmlsZSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS1VSy1Ob3J0aEVhc3QtMjAyMi0wMi0yNi5HZW5kZXJPcmllbnRhdGlvbi1NU1RyZWUuaW5rc2NhcGVkLitub2RlLWNvdW50cytHQk1TTS5zdmciKQoKCmBgYApcClNwZWNpZnkgZGlyZWN0b3J5IHRvIG91dHB1dCBwbG90cwpgYGB7cn0KRmlndXJlX291dHB1dF9kaXJlY3RvcnkgPC0gcGFzdGUwKGdldHdkKCksICIvRmlndXJlc19yZXZpc2lvbl8wMy0yMDIzLyIpCgojIi9Vc2Vycy9tYjI5L1BhcGVycy9UcmVwb25lbWFfVUstUEhFLWdlbi1lcGlfMjAyMS9GaWd1cmVzL0ZpZ3VyZV9EcmFmdGluZy9Xb3JraW5nX0ZpZ3VyZXNfMDgtMjAyMi8iCmBgYApcClJlYWQgaW4gdHJlZXMKYGBge3J9ClRQQS5NTHRyZWUgPC0gbWlkcG9pbnQucm9vdChyZWFkLnRyZWUoVFBBLk1MdHJlZS5maWxlKSkKVFBBLnB5amFyLnRyZWUgPC0gbWlkcG9pbnQucm9vdChyZWFkLnRyZWUoVFBBLnB5amFyLmZpbGUpKQpgYGAKXApSZWFkIGluIGZpbmFsIG91dHB1dCBtZXRhZGF0YSBmcm9tIEdsb2JhbCBVYmVyIHN0dWR5IChCZWFsZSAyMDIxKQpgYGB7cn0KVFBBLm1ldGEyLjEgPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKFRQQS5tZXRhMi5maWxlLHNoZWV0PSJTdXBwbGVtZW50YXJ5X0RhdGExX1NhbXBsZS1NZXRhIikKYGBgClwKQ3JlYXRlIGEgY29sb3VyIHNjaGVtZSBmb3IgTGluZWFnZXMsIENvdW50cmllcyBhbmQgQ29udGluZW50cyAoY29uc2lzdGVudCB3aXRoIEJlYWxlLCAyMDIxKQpgYGB7cn0KIyBDb2xvdXJpbmcgZm9yIGNvdW50cnkKY29udGluZW50YWwuY291bnRyeS5jb2xzLmJyZXcyIDwtIHVuaXF1ZShUUEEubWV0YTIuMVssYygiR2VvX0NvdW50cnkiLCJDb250aW5lbnQiKV0pCmNvbnRpbmVudGFsLmNvdW50cnkuY29scy5icmV3MiA8LSBjb250aW5lbnRhbC5jb3VudHJ5LmNvbHMuYnJldzJbb3JkZXIoY29udGluZW50YWwuY291bnRyeS5jb2xzLmJyZXcyJENvbnRpbmVudCxjb250aW5lbnRhbC5jb3VudHJ5LmNvbHMuYnJldzIkR2VvX0NvdW50cnkpLF0KCmNvbnRpbmVudGFsLmNvdW50cnkuY29scy5icmV3MiRjb3VudHJ5LmNvbCA8LSBjKCIjZWM3MDE0IiwiI2ZlYzQ0ZiIsIiNkZTJkMjYiLCIjZmI2YTRhIiwiI2JkYmRiZCIsIiM3MzczNzMiLGJyZXdlci5wYWwobj04LCJQdXJwbGVzIilbNDo4XSxicmV3ZXIucGFsKG49OCwiQmx1ZXMiKVszOjhdLGJyZXdlci5wYWwobj01LCJHcmVlbnMiKVszOjVdLCIjYzUxYjhhIiwiIzhjNTEwYSIpCgojIENvbG91cmluZyBmb3IgQ29udGluZW50CmNvbnRpbmVudGFsLmNvbHMuYnJldzIgPC0gZGF0YS5mcmFtZShDb250aW5lbnQ9c29ydCh1bmlxdWUoVFBBLm1ldGEyLjEkQ29udGluZW50KSksc3RyaW5nc0FzRmFjdG9ycz1GKQpjb250aW5lbnRhbC5jb2xzLmJyZXcyJGNvbnRpbmVudC5jb2wgPC0gYygiI2ZlYzQ0ZiIsIiNkZTJkMjYiLCIjYmRiZGJkIiwiIzIxNzFiNSIsIiM3NGM0NzYiLCIjYzUxYjhhIiwiI2VjNzAxNCIpCgoKIyBDb2xvdXJpbmcgZm9yIFRQQSBMaW5lYWdlClRQQV9MaW5lYWdlLmNvbHMgPC0gZGF0YS5mcmFtZShMaW5lYWdlPXNvcnQodW5pcXVlKFRQQS5tZXRhMi4xJFRQQV9MaW5lYWdlKSksc3RyaW5nc0FzRmFjdG9ycz1GKQpUUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UuY29sIDwtIGMoInJveWFsYmx1ZTIiLCAiaW5kaWFucmVkMSIpCiNjKCIjNDM2ZWVlIiwgIiM2NjY2NjYiLCIjZmY2YTZhIikKVFBBX0xpbmVhZ2UuY29scyRMaW5lYWdlIDwtIGZhY3RvcihUUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UsIGxldmVscz1jKCJOaWNob2xzIiwiU1MxNCIsIm91dGxpZXIiKSkKCiMgTGluZWFnZSBIZXhjb2RlcwojIHJveWFsYmx1ZTIgIzQzNmVlZQojIGluZGlhbnJlZDEgI2ZmNmE2YQpgYGAKXApEZWZpbmUgY29sb3VycyBmb3Igc3VibGluZWFnZXMKYGBge3J9CiMgRGVmaW5lIHN1YmxpbmVhZ2UgY2x1c3RlcmluZyBzY2hlbWUgdXNpbmcgYnJldyBjb2xvdXJzY2FsZXMKc3VibGluZWFnZXMuY29scy5icmV3IDwtIGRhdGEuZnJhbWUodW5pcXVlKFRQQS5tZXRhMi4xWyxjKCJUUEFfTGluZWFnZSIsIlRQQS5waW5lY29uZS5zdWJsaW5lYWdlIildKSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnN1YmxpbmVhZ2VzLmNvbHMuYnJldyA8LSBzdWJsaW5lYWdlcy5jb2xzLmJyZXdbb3JkZXIoc3VibGluZWFnZXMuY29scy5icmV3JFRQQV9MaW5lYWdlLHN1YmxpbmVhZ2VzLmNvbHMuYnJldyRUUEEucGluZWNvbmUuc3VibGluZWFnZSksXQoKc3VibGluZWFnZXMuY29scy5icmV3JHN1Ymxpbi5vcmRlciA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihzdWJsaW5lYWdlcy5jb2xzLmJyZXckVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpKQpzdWJsaW5lYWdlcy5jb2xzLmJyZXcgPC0gc3VibGluZWFnZXMuY29scy5icmV3W29yZGVyKHN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW4ub3JkZXIpLF0KCiMgRm9yIHJldmlzZWQgYm9vdHN0cmFwcGVkIGNsdXN0ZXJzCnN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLmNvbHMgPC0gYygiI0ZDOTI3MiIsIiNFRjNCMkMiLGJyZXdlci5wYWwobj00LCJHcmVlbnMiKVsyOjRdLGJyZXdlci5wYWwobj00LCJZbE9yQnIiKVtjKDIsMyldLGJyZXdlci5wYWwobj02LCJCbHVlcyIpWzI6Nl0sYnJld2VyLnBhbChuPTYsIlB1cnBsZXMiKVsyOjZdLCJncmV5ODAiLCJncmV5ODAiLCJncmV5ODAiLCJncmV5ODAiKQogIApzdWJsaW5lYWdlcy5jb2xzLmJyZXcgPC0gdW5pcXVlKHN1YmxpbmVhZ2VzLmNvbHMuYnJld1ssYygiVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UiLCJzdWJsaW5lYWdlLmNvbHMiKV0pCnN1YmxpbmVhZ2VzLmNvbHMuYnJldyA8LSBzdWJsaW5lYWdlcy5jb2xzLmJyZXdbb3JkZXIoYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoc3VibGluZWFnZXMuY29scy5icmV3JFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSkpLF0Kc3VibGluZWFnZXMuY29scy5icmV3JFRQQS5waW5lY29uZS5zdWJsaW5lYWdlIDwtIGZhY3RvcihzdWJsaW5lYWdlcy5jb2xzLmJyZXckVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGxldmVscz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpCnN1YmxpbmVhZ2VzLmNvbHMuYnJldyA8LSBzdWJsaW5lYWdlcy5jb2xzLmJyZXdbIWlzLm5hKHN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlKSxdCgpjb2xuYW1lcyhzdWJsaW5lYWdlcy5jb2xzLmJyZXcpIDwtIGMoInN1YmxpbmVhZ2UiLCJzdWJsaW5lYWdlLmNvbHMiKQpzdWJsaW5lYWdlcy5jb2xzLmJyZXcgPC0gdW5pcXVlKHN1YmxpbmVhZ2VzLmNvbHMuYnJldykKYGBgClwKUmVzdHJpY3QgYW5hbHlzaXMgdG8gaGlnaCBxdWFsaXR5IGdlbm9tZXMgKGFuZCB0cmVlKQpgYGB7cn0KVFBBLm1ldGEyLjEgPC0gVFBBLm1ldGEyLjFbVFBBLm1ldGEyLjEkZmluZXNjYWxlLmFuYWx5c2lzPT0iWWVzIixdCmBgYApcCkNyZWF0ZSBhICJVSyIgdmFyaWFibGUsIGFuZCBhICJQSEUiIHZhcmlhYmxlCmBgYHtyfQpUUEEubWV0YTIuMSRpcy5VSyA8LSBpZmVsc2UoVFBBLm1ldGEyLjEkR2VvX0NvdW50cnk9PSJVSyIsIlVLIiwiT3RoZXIiKQpUUEEubWV0YTIuMSRpcy5QSEUgPC0gaWZlbHNlKFRQQS5tZXRhMi4xJEdlb19Db3VudHJ5PT0iVUsiICYgZ3JlcGwoIlBIRSIsVFBBLm1ldGEyLjEkU2FtcGxlX05hbWUpLCJQSEUiLCJPdGhlciIpCmBgYApcCmBgYHtyfQojIFByZXBhcmUgTUwgdHJlZQpUUEEuTUx0cmVlLmdndHJlZSA8LSBnZ3RyZWUoVFBBLk1MdHJlZSxsYXlvdXQgPSAiZmFuIixvcGVuLmFuZ2xlID0gMTAsIHJpZ2h0PVQpCgojIFByZXBhcmUgY291bnRyeSBkYXRhc2V0ClRQQS5yYXdzZXEuY291bnRyaWVzLnAgPC0gZGF0YS5mcmFtZShyb3cubmFtZXM9VFBBLm1ldGEyLjEkU2FtcGxlX05hbWUsIENvdW50cnk9VFBBLm1ldGEyLjEkR2VvX0NvdW50cnksIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKIyBQcmVwYXJlIGNvbnRpbmVudCBkYXRhc2V0ClRQQS5yYXdzZXEuY29udGluZW50cy5wIDwtIGRhdGEuZnJhbWUocm93Lm5hbWVzPVRQQS5tZXRhMi4xJFNhbXBsZV9OYW1lLCBDb250aW5lbnQ9VFBBLm1ldGEyLjEkQ29udGluZW50LCBzdHJpbmdzQXNGYWN0b3JzID0gRikKCiMgUHJlcGFyZSBVSyBkYXRhIHN0cmlwClRQQS5yYXdzZXEuVUsucCA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcz1UUEEubWV0YTIuMSRTYW1wbGVfTmFtZSwgRW5nbGFuZD1UUEEubWV0YTIuMSRpcy5VSywgc3RyaW5nc0FzRmFjdG9ycyA9IEYpClRQQS5yYXdzZXEuVUsucFtUUEEucmF3c2VxLlVLLnAkRW5nbGFuZD09IlVLIixdIDwtICJFbmdsYW5kIgoKIyBQcmVwYXJlIFBIRSBkYXRhIHN0cmlwClRQQS5yYXdzZXEuUEhFLnAgPC0gZGF0YS5mcmFtZShyb3cubmFtZXM9VFBBLm1ldGEyLjEkU2FtcGxlX05hbWUsIFBIRT1UUEEubWV0YTIuMSRpcy5QSEUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKIyBQcmVwYXJlIE1ham9yIGxpbmVhZ2UgZGF0YXNldApUUEEucmF3c2VxLkxpbmVhZ2UucCA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcz1UUEEubWV0YTIuMSRTYW1wbGVfTmFtZSwgTGluZWFnZT1UUEEubWV0YTIuMSRUUEFfTGluZWFnZSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCgojIFByZXBhcmUgc3VibGluZWFnZSBsaW5lYWdlIGRhdGFzZXQKVFBBLnJhd3NlcS5zdWJMaW5lYWdlLnAgPC0gZGF0YS5mcmFtZShyb3cubmFtZXM9VFBBLm1ldGEyLjEkU2FtcGxlX05hbWUsIFN1YmxpbmVhZ2U9VFBBLm1ldGEyLjEkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKCiMgUHJlcGFyZSBZZWFyIGRhdGFzZXQgKGFsbCBzYW1wbGVzKQpUUEEucmF3c2VxLmFsbC5ZZWFycy5wIDwtIGRhdGEuZnJhbWUocm93Lm5hbWVzPVRQQS5tZXRhMi4xJFNhbXBsZV9OYW1lLCBZZWFyPVRQQS5tZXRhMi4xJFNhbXBsZV9ZZWFyLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKCgpmbG9vcl81eWVhcnMgIDwtIGZ1bmN0aW9uKHZhbHVlKXsgcmV0dXJuKHZhbHVlIC0gdmFsdWUgJSUgNSkgfQpUUEEubWV0YTIuMSRTYW1wbGVfNXllYXIud2luZG93IDwtIHBhc3RlMChmbG9vcl81eWVhcnMoYXMubnVtZXJpYyhUUEEubWV0YTIuMSRTYW1wbGVfWWVhcikpLCItIixmbG9vcl81eWVhcnMoYXMubnVtZXJpYyhUUEEubWV0YTIuMSRTYW1wbGVfWWVhcikpKzUpCiMgU29tZSBzYW1wbGVzIGhhdmUgdW5jZXJ0YWluIGRhdGVzICh1cCB0byAyMC0zMCB5ZWFycyB1bmNlcnRhaW50eSksIGJ1dCBmb3IgdGhlIHB1cnBvc2VzIG9mIHRoZXNlIHBsb3R0aW5nIGNhdGVnb3JpZXMgd2UnbGwgdXNlIHRoZSBjZW50cmVwb2ludCB5ZWFyClRQQS5tZXRhMi4xJFNhbXBsZV81eWVhci53aW5kb3cgPC0gc2FwcGx5KDE6bnJvdyhUUEEubWV0YTIuMSksIGZ1bmN0aW9uKHgpIGlmZWxzZShUUEEubWV0YTIuMSRTYW1wbGVfWWVhclt4XT09Ii0iLE5BLCBpZmVsc2UoaXMubmEoVFBBLm1ldGEyLjEkU2FtcGxlXzV5ZWFyLndpbmRvd1t4XSksTkEsIGlmZWxzZShUUEEubWV0YTIuMSRTYW1wbGVfWWVhclt4XT09IjE5NTAtMTk4MCIsIjE5NjUtMTk3MCIsaWZlbHNlKFRQQS5tZXRhMi4xJFNhbXBsZV9ZZWFyW3hdPT0iMTk2MC0xOTgwIiwiMTk2NS0xOTcwIiAsaWZlbHNlKFRQQS5tZXRhMi4xJFNhbXBsZV9ZZWFyW3hdPT0iMTk4MC0xOTk5IiwiMTk4NS0xOTkwIixUUEEubWV0YTIuMSRTYW1wbGVfNXllYXIud2luZG93W3hdKSkpKSkpCgoKVFBBLm1ldGEyLjEkU2FtcGxlX3llYXIuMTk5MC5jdXR0b2ZmIDwtIGlmZWxzZShUUEEubWV0YTIuMSRTYW1wbGVfWWVhcj4xOTkwLFRQQS5tZXRhMi4xJFNhbXBsZV9ZZWFyLCI8MTk5MCIpCgpUUEEubWV0YTIuMSRTYW1wbGVfeWVhci4xOTk5LmN1dHRvZmYgPC0gaWZlbHNlKFRQQS5tZXRhMi4xJFNhbXBsZV9ZZWFyPjE5OTksVFBBLm1ldGEyLjEkU2FtcGxlX1llYXIsIjwxOTk5IikKVFBBLnJhd3NlcS55ZWFyLmN1dHRvZmYucCA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcz1UUEEubWV0YTIuMSRTYW1wbGVfTmFtZSwgU2FtcGxlLlllYXI9VFBBLm1ldGEyLjEkU2FtcGxlX3llYXIuMTk5OS5jdXR0b2ZmLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKCmBgYApcClwKIyBCcmluZyBpbiBQSEUgbWV0YWRhdGEKYGBge3J9ClBIRS5tZXRhZGF0YS5saW5rZWQgPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKFBIRS5tZXRhZGF0YS5saW5rZWQuZmlsZSkKYGBgClwKRG8gc29tZSBjbGVhbnVwIGFuZCBmYWN0b3Jpbmcgb2YgdmFyaWFibGVzCmBgYHtyfQoKUEhFLm1ldGFkYXRhLmxpbmtlZCRhZ2VfZ3JvdXAgPC0gZmFjdG9yKFBIRS5tZXRhZGF0YS5saW5rZWQkYWdlX2dyb3VwLCBsZXZlbHM9cmV2KGMoIjE2LTI0IiwiMjUtMzQiLCIzNS00NCIsIjQ1KyIsIlVua25vd24iKSkpCgpQSEUubWV0YWRhdGEubGlua2VkJGxvbmRvbiA8LSBmYWN0b3IoUEhFLm1ldGFkYXRhLmxpbmtlZCRsb25kb24sbGV2ZWxzPXJldihjKCJZZXMiLCJObyIsIlVua25vd24iKSkpClBIRS5tZXRhZGF0YS5saW5rZWQkdWtib3JuIDwtIGZhY3RvcihQSEUubWV0YWRhdGEubGlua2VkJHVrYm9ybixsZXZlbHM9cmV2KGMoIlllcyIsIk5vIiwiVW5rbm93biIpKSkKUEhFLm1ldGFkYXRhLmxpbmtlZCRoaXZwb3MgPC0gZmFjdG9yKFBIRS5tZXRhZGF0YS5saW5rZWQkaGl2cG9zLCBsZXZlbHM9cmV2KGMoIlllcyIsIk5vIiwiVW5rbm93biIpKSkKCiMgbmVlZCB0byB1cGRhdGUgdGVybWlub2xvZ3kgb2YgJ01TTScgdG8gJ0dCTVNNJwpQSEUubWV0YWRhdGEubGlua2VkW1BIRS5tZXRhZGF0YS5saW5rZWQkZ2VuZGVyX29yaWVudGF0aW9uPT0iTVNNIiwiZ2VuZGVyX29yaWVudGF0aW9uIl0gPC0gIkdCTVNNIgpQSEUubWV0YWRhdGEubGlua2VkJGdlbmRlcl9vcmllbnRhdGlvbiA8LSBmYWN0b3IoUEhFLm1ldGFkYXRhLmxpbmtlZCRnZW5kZXJfb3JpZW50YXRpb24sIGxldmVscz1yZXYoYygiTVNXIiwiR0JNU00iLCJXU00iLCJNVW5rbm93biIsIlVua25vd24iKSkpCgpQSEUubWV0YWRhdGEubGlua2VkJHBoZV9jZW50cmUgPC0gZmFjdG9yKFBIRS5tZXRhZGF0YS5saW5rZWQkcGhlX2NlbnRyZSwgbGV2ZWxzPXJldihjKCJFYXN0IE1pZGxhbmRzIiwgIkVhc3Qgb2YgRW5nbGFuZCIsICJMb25kb24iLCAiTm9ydGggRWFzdCIsICJOb3J0aCBXZXN0IiwgIlNvdXRoIEVhc3QiLCAiU291dGggV2VzdCIsICJXZXN0IE1pZGxhbmRzIiwgIllvcmtzaGlyZSBhbmQgSHVtYmVyIiwgIlVLIChub3QgRW5nbGFuZCkiLCAiTm90IEtub3duIikpKQoKUEhFLm1ldGFkYXRhLmxpbmtlZCRUUEEucGluZWNvbmUuc3VibGluZWFnZSA8LSAgZmFjdG9yKFBIRS5tZXRhZGF0YS5saW5rZWQkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGxldmVscz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSkKCmBgYApcClwKIyMjIEV4dHJhY3QgaW5mb3JtYXRpb24gYWJvdXQgZHVwbGljYXRlcwpgYGB7cn0KUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZFshaXMubmEoUEhFLm1ldGFkYXRhLmxpbmtlZCRkdXBfZmxhZyksXQpQSEUubWV0YWRhdGEuZHVwbGljYXRlcyA8LSBQSEUubWV0YWRhdGEuZHVwbGljYXRlc1shaXMubmEoUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXMkU2FtcGxlX05hbWUpLF0KCgpQSEUucGF0aWVudC5tYXRjaGVzIDwtIGRhdGEuZnJhbWUoCiAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHVwX2ZsYWcgPSBjKCIxQSIsIjFCIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjJBIiwiMkIiLCIzQSIsIjNCIiwiNEEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiNEIiLCI1QSIsIjVCIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHVwX1BhdGllbnQgPSBjKCJQYXRpZW50IDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUGF0aWVudCAxIiwiUGF0aWVudCAyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBhdGllbnQgMiIsIlBhdGllbnQgMyIsIlBhdGllbnQgMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQYXRpZW50IDQiLCJQYXRpZW50IDQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUGF0aWVudCA1IiwiUGF0aWVudCA1IiksCiAgICAgICAgICAgICAgICAgICAgICAgICBkdXBfUGF0aWVudF9TYW1wbGUgPSBjKCJzYW1wbGUgMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzYW1wbGUgMiIsInNhbXBsZSAxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNhbXBsZSAyIiwic2FtcGxlIDEiLCJzYW1wbGUgMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzYW1wbGUgMSIsInNhbXBsZSAyIiwic2FtcGxlIDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2FtcGxlIDIiKQogICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAKClBIRS5tZXRhZGF0YS5kdXBsaWNhdGVzIDwtIGxlZnRfam9pbihQSEUubWV0YWRhdGEuZHVwbGljYXRlcywgUEhFLnBhdGllbnQubWF0Y2hlcywgYnk9ImR1cF9mbGFnIikKClBIRS5tZXRhZGF0YS5kdXBsaWNhdGVzCmBgYAoKRHVwbGljYXRlIFNhbXBsZXMgbWlzc2luZyBtZXRhZGF0YSBhcmUgYWxsICduZXcgZHVwbGljYXRlcycgYW5kIHdlcmUgZXhjbHVkZWQgZHVlIHRvIGxvdyBtYXBwaW5nIGNvdmVyYWdlIChhbGwgY2hlY2tlZCkuClwKU2FtcGxlcyBsYWJlbGxlZCAnWkEnIGFuZCAnWEInIGhhZCBkdXBsaWNhdGVzIGluIHRoZSBvcmlnaW5hbCBkYXRhc2V0LCBidXQgdGhlIHJlY2lwcm9jYWwgcGFpcnMgd2VyZSBleGNsdWRlZCBkdWUgdG8gcXVhbGl0eSBpc3Vlcy4KXApBdmFpbGFibGUgcGFpcnMgLSBQYXRpZW50IDMsIFBhdGllbnQgNAoKYGBge3J9ClBIRS5tZXRhZGF0YS5kdXBsaWNhdGVzLnBhaXJlZCA8LSBQSEUubWV0YWRhdGEuZHVwbGljYXRlc1tQSEUubWV0YWRhdGEuZHVwbGljYXRlcyRkdXBfUGF0aWVudCAlaW4lIGMoIlBhdGllbnQgMyIsIlBhdGllbnQgNCIpLF0KUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXMucGFpcmVkW29yZGVyKFBIRS5tZXRhZGF0YS5kdXBsaWNhdGVzLnBhaXJlZCRkdXBfUGF0aWVudCwgUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXMucGFpcmVkJHllYXIsUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXMucGFpcmVkJG1vbnRoKSxjKCJTYW1wbGVfTmFtZSIsImR1cF9QYXRpZW50IiwgIm1vbnRoLmZpeCIsICJ5ZWFyIildCmBgYApcClRoZXNlIHdpbGwgYmUgcmV2aXNpdGVkIGxhdGVyIGluIHRoZSBhbmFseXNpcy4gClwKUGF0aWVudCA0CkhJVi12ZSBNU00gKDQ1KyksIFVLIGJvcm4sIFBIRSByZWdpb24gRAoyIHNhbXBsZXMsIGNvbGxlY3RlZCBpbiB0aGUgc2FtZSBtb250aCBhbmQgeWVhcgpCb3RoIHNhbXBsZXMgYXJlIHN1YmxpbmVhZ2UgMSwgYW5kIGlkZW50aWNhbCAoMCBwd1NOUHMpCkxpa2VseSB0aGUgc2FtZSBpbmZlY3Rpb24gKGRlcGVuZGluZyBvbiBkYXRlcywgdHJlYXRtZW50LCBldGMpLCBidXQgY2Fu4oCZdCBydWxlIG91dCByZWluZmVjdGlvbiB3aXRoIHNhbWUgc3RyYWluLgpcClBhdGllbnQgMwpISVYtdmUgTVNNICgzNS00NCksIG5vdCBVSyBib3JuLCBiYXNlZCBpbiBMb25kb24gKEMpCjIgc2FtcGxlcywgY29sbGVjdGVkIDkgbW9udGhzIGFwYXJ0CkJvdGggc2FtcGxlcyBhcmUgc3VibGluZWFnZSAxLCBidXQgaGF2ZSA3IHBhaXJ3aXNlIFNOUHMgYmV0d2VlbiB0aGVtIChsb2FkcyEpClJlaW5mZWN0aW9uIOKAkyBwcm9iYWJseSBmcm9tIGEgZGlmZmVyZW50IHRyYW5zbWlzc2lvbiBuZXR3b3JrClwKXApIb3dldmVyLCBiYXNlZCBvbiB0aGUgc2FtcGxlIGRhdGVzLCBhcyB3ZWxsIGFzIHRoZSBvdXRjb21lIG9mIHRoZSBkb3duc3RyZWFtIGdlbmV0aWMgYW5hbHlzaXMsIHdlIGNhbiBzZWUgdGhhdCBQYXRpZW50IDMgaGFzIGR1cGxpY2F0ZSBpbmZlY3Rpb24gZXZlbnRzIChkaWZmZXJlbnQgZGF0ZXMsIDEwIG1vbnRocyBhcGFydCkgYW5kIHRoZSBnZW5vbWVzIGFyZSBkaXN0aW5jdCAoNyBTTlBzIGFwYXJ0KSwgd2hlcmVhcyBQYXRpZW50IDQgc2FtcGxlcyB3ZXJlIGNvbGxlY3RlZCBpbiB0aGUgc2FtZSBtb250aCBhbmQgeWVhciAoaS5lLiBhcmUgbGlrZWx5IGR1cGxpY2F0ZXMgZnJvbSB0aGUgc2FtZSBpbmZlY3Rpb24pIGFuZCBoYXMgaWRlbnRpY2FsIGdlbm9tZXMuClwKRm9yIGRvd25zdHJlYW0gYW5hbHlzaXMgcHVycG9zZXMsIHdlIHdpbGwgcmV0YWluIGJvdGggc2FtcGxlcyBmb3IgUGF0aWVudCAzIChkaXNjcmV0ZSBpbmZlY3Rpb25zKSwgYnV0IGV4Y2x1ZGUgb25lIHNhbXBsZSBmcm9tIFBhdGllbnQgNCAoZHVwbGljYXRlIGluZmVjdGlvbiBzYW1wbGVzKSAtICdQSEUxNTAxMjZBJyBoYXMgbXVjaCBiZXR0ZXIgZ2Vub21lIGNvdmVyYWdlLCBzbyBleGNsdWRlICdQSEUxNTAxMjVBJwpcClwKIyMjIEZ1cnRoZXIgRXhjbHVzaW9ucyBcClBIRTEzMDA1NkEgLSBkdXBsaWNhdGUgb2YgUEhFMTMwMDU3QiAoYWxyZWFkeSByZW1vdmVkLCBzbyBub3QgcmVsZXZhbnQpIC0gZG9uJ3QgZXhjbHVkZSEKUEhFMTcwNDAyQSAtIHF1YWxpdHkgY29udHJvbCBzYW1wbGUKUEhFMTcwMzc4QSAtIHF1YWxpdHkgY29udHJvbCBzYW1wbGUKClwKRXhjbHVkZSBkdXBsaWNhdGUgc2VxdWVuY2VzCmBgYHtyfQpkdXBsaWNhdGUuZXhjbHVzaW9uLmxpc3QgPC0gYygiUEhFMTUwMTI1QSIsIlBIRTE3MDQwMkEiLCJQSEUxNzAzNzhBIikKUEhFLm1ldGFkYXRhLmxpbmtlZCA8LSBQSEUubWV0YWRhdGEubGlua2VkW1BIRS5tZXRhZGF0YS5saW5rZWQkU2FtcGxlX05hbWUgJW5vdGluJSBkdXBsaWNhdGUuZXhjbHVzaW9uLmxpc3QsXQpgYGAKXAoKIyMjIE1vdmluZyBvbi4uLiBcCgpEZWZpbmUgc29tZSBjb2xvdXIgc2NoZW1lcwpgYGB7cn0KIyBkZWZpbmUgc29tZSBjb2xvcnMgZm9yIGVhY2ggcmVnaW9uClBIRS5yZWdpb24uY29scy5icmV3IDwtIGRhdGEuZnJhbWUoVUtIU0EucmVnaW9uPWMoIk5vcnRoIEVhc3QiLCAiTm9ydGggV2VzdCIsICJZb3Jrc2hpcmUgYW5kIEh1bWJlciIsICJFYXN0IE1pZGxhbmRzIiwgIldlc3QgTWlkbGFuZHMiLCAiRWFzdCBvZiBFbmdsYW5kIiwgIkxvbmRvbiIsICJTb3V0aCBFYXN0IiwiU291dGggV2VzdCIsIlVLIChub3QgRW5nbGFuZCkiLCAiTm90IEtub3duIiksIHN0cmluZ3NBc0ZhY3RvcnM9RikKUEhFLnJlZ2lvbi5jb2xzLmJyZXckcmVnaW9uLmNvbCA8LSBjKCIjQTZDRUUzIiwiIzFGNzhCNCIsIiNDQUIyRDYiLCIjMzNBMDJDIiwiI0IyREY4QSIsIiNGRjdGMDAiLCIjRTMxQTFDIiwiI0ZCOUE5OSIsIiNENEJCMDIiLCJncmV5NzUiLCJncmV5MjUiKQoKIyBISVYgY29sb3Igc2NoZW1lClBIRS5oaXYuY29scyA8LSBkYXRhLmZyYW1lKGhpdnBvcz1yZXYoc29ydCh1bmlxdWUoUEhFLm1ldGFkYXRhLmxpbmtlZCRoaXZwb3MpKSksIHN0cmluZ3NBc0ZhY3RvcnM9RikKUEhFLmhpdi5jb2xzJGhpdi5jb2xzIDwtIGMoIiMxZjc4YjQiLCIjYjJkZjhhIiwiZ3JleTc1IikKCiMgT3JpZW50YXRpb24gY29sb3VyIHNjaGVtZQpQSEUub3JpZW50YXRpb24uY29scyA8LSBkYXRhLmZyYW1lKG9yaWVudGF0aW9uPXJldihzb3J0KHVuaXF1ZShQSEUubWV0YWRhdGEubGlua2VkJGdlbmRlcl9vcmllbnRhdGlvbikpKSwgc3RyaW5nc0FzRmFjdG9ycz1GKQpQSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbiA8LSBmYWN0b3IoUEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24sIGxldmVscz1yZXYoc29ydCh1bmlxdWUoUEhFLm1ldGFkYXRhLmxpbmtlZCRnZW5kZXJfb3JpZW50YXRpb24pKSksIGxhYmVscz1jKCJNU1ciLCJHQk1TTSIsIldTTSIsIk1Vbmtub3duIiwiVW5rbm93biIpKQpQSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbi5jb2xzIDwtIGMoIiMxZjc4YjQiLCIjYjJkZjhhIiwiI2ZiOWE5OSIsIiNhNmNlZTMiLCJncmV5NzUiKQoKIyBVSyBib3JuIGNvbG91ciBzY2hlbWUKUEhFLnVrYm9ybi5jb2xzIDwtIGRhdGEuZnJhbWUodWtib3JuPXJldihzb3J0KHVuaXF1ZShQSEUubWV0YWRhdGEubGlua2VkJHVrYm9ybikpKSx1a2Jvcm4uY29scz1jKCIjMWY3OGI0IiwiI2IyZGY4YSIsImdyZXk3NSIpLHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKIyBMb25kb24gYmFzZWQgY29sb3VyIHNjaGVtZQpQSEUubG9uZG9uLmNvbHMgPC0gZGF0YS5mcmFtZShsb25kb249cmV2KHNvcnQodW5pcXVlKFBIRS5tZXRhZGF0YS5saW5rZWQkbG9uZG9uKSkpLGxvbmRvbi5jb2xzPWMoIiMxZjc4YjQiLCIjYjJkZjhhIiwiZ3JleTc1Iiksc3RyaW5nc0FzRmFjdG9ycyA9IEYpCgoKIyBBZ2UgZ3JvdXAgY29sb3VyIHNjaGVtZQpQSEUuQWdlLmNvbHMgPC0gZGF0YS5mcmFtZShhZ2VfZ3JvdXA9cmV2KHNvcnQodW5pcXVlKFBIRS5tZXRhZGF0YS5saW5rZWQkYWdlX2dyb3VwKSkpLHN0cmluZ3NBc0ZhY3RvcnMgPSBUKQpQSEUuQWdlLmNvbHMkYWdlX2dyb3VwLmNvbHMgPC0gYyhicmV3ZXIucGFsKG49NCwiWWxHbkJ1IiksImdyZXk3NSIpCgojIFNhbXBsZSBEYXRlIGNvbG91ciBzY2hlbWUKUEhFLnllYXIuY29scyA8LSBkYXRhLmZyYW1lKHllYXI9KHNvcnQodW5pcXVlKFBIRS5tZXRhZGF0YS5saW5rZWQkeWVhcikpKSxzdHJpbmdzQXNGYWN0b3JzID0gVCkKUEhFLnllYXIuY29scyR5ZWFyLmNvbHMgPC0gYnJld2VyLnBhbChuPTcsIllsT3JSZCIpCgojIFNhbXBsZSBEYXRlIChhbGwgZ2xvYmFsIGRhdGEsIGJ1dCB3aXRoIDE5OTAgY3V0dG9mZikKVFBBLnllYXIuY3V0dG9mZi5jb2xzIDwtIGRhdGEuZnJhbWUoZGF0ZS5jdXR0b2ZmPWMoIjwxOTk5IiwxOTk5OjIwMTkpLCBkYXRlLmN1dHRvZmYuY29sPWMoIiNGMkYyRjIiLGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbCg3LCAiWWxPclJkIikpKGxlbmd0aCgxOTk5OjIwMTkpKSkpCgoKYGBgClwKXAojIyMjIwojIyBGaXJzdCBkZXNjcmliZSB0aGUgc2VxdWVuY2VkIHBvcHVsYXRpb24gYXMgYSB3aG9sZQpcClNldCBvcmRlciBvZiBQSEUgcmVnaW9ucwpgYGB7cn0KUEhFLm1ldGFkYXRhLmxpbmtlZCRwaGVfY2VudHJlIDwtIGZhY3RvcihQSEUubWV0YWRhdGEubGlua2VkJHBoZV9jZW50cmUsIGxldmVscz1yZXYoUEhFLnJlZ2lvbi5jb2xzLmJyZXckVUtIU0EucmVnaW9uKSkKYGBgClwKR2VuZXJhdGUgc29tZSBiYXNpYyBzdGF0aXN0aWNzIGFib3V0IGdlb2dyYXBoaWNhbCBQSEUgcmVnaW9ucyAoYW5vbnltaXNlZCkKYGBge3J9ClBIRS5jb3VudC5hbGwgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudC5wZXIucmVnaW9uPW4oKSkKClBIRS5jb3VudC55ZWFycyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoeWVhcikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudC5wZXIueWVhcj1uKCkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBkcGx5cjo6bXV0YXRlKHBlcmMucGVyLnllYXI9KGNvdW50LnBlci55ZWFyL3N1bShjb3VudC5wZXIueWVhcikpKjEwMCkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBISVYgc3RhdHVzClBIRS5ISVYuY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShoaXZwb3MpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnJlZ2lvbikgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhoaXZwb3MpLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShjdW1fZnJhY3QgPSBjdW1zdW0oZnJhY3Rpb24pKSAlPiUKICBkcGx5cjo6bXV0YXRlKGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKSAlPiUKICBkcGx5cjo6bXV0YXRlKEhJVi5wZXJjPShDb3VudC9zdW0oQ291bnQpKjEwMCkpCiAgCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBnZW5kZXIgb3JpZW50YXRpb24KUEhFLm9yaWVudGF0aW9uLmNvdW50cyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoZ2VuZGVyX29yaWVudGF0aW9uKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhnZW5kZXJfb3JpZW50YXRpb24pLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkgJT4lIAogIGRwbHlyOjptdXRhdGUob3JpZW50YXRpb24ucGVyYz0oQ291bnQvc3VtKENvdW50KSoxMDApKQoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IFVLIGJvcm4gKHZhZ3VlIGNhdGVnb3J5IHRoYXQncyB1bmZvcnR1bmF0ZWx5IG9ubHkgbWFyZ2luYWxseSBoZWxwZnVsKQpQSEUuVUtib3JuLmNvdW50cyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkodWtib3JuKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyh1a2Jvcm4pLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkgJT4lIAogIGRwbHlyOjptdXRhdGUoVUtib3JuLnBlcmM9KENvdW50L3N1bShDb3VudCkqMTAwKSkKICAKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IExvbmRvbiBiYXNlZApQSEUuTG9uZG9uLmNvdW50cyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkobG9uZG9uKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhsb25kb24pLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkgJT4lCiAgZHBseXI6Om11dGF0ZShMb25kb24ucGVyYz0oQ291bnQvc3VtKENvdW50KSoxMDApKQoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IEFnZSBncm91cApQSEUuQWdlLmNvdW50cyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoYWdlX2dyb3VwKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhhZ2VfZ3JvdXApLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkgJT4lCiAgZHBseXI6Om11dGF0ZShBZ2UucGVyYz0oQ291bnQvc3VtKENvdW50KSoxMDApKQoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IExpbmVhZ2UgZ3JvdXAKUEhFLkxpbmVhZ2UuY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEFfTGluZWFnZSkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwucmVnaW9uPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoVFBBX0xpbmVhZ2UpLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkgJT4lCiAgZHBseXI6Om11dGF0ZShMaW5lYWdlLnBlcmM9KENvdW50L3N1bShDb3VudCkqMTAwKSkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBzdWJsaW5lYWdlIGdyb3VwClBIRS5zdWJsaW5lYWdlLmNvdW50cyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwucmVnaW9uLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpICU+JQogIGRwbHlyOjptdXRhdGUoU3VibGluZWFnZS5wZXJjPShDb3VudC9zdW0oQ291bnQpKjEwMCkpCmBgYApcCk1ha2Ugc29tZSBwbG90cwpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTh9CiMgTWFrZSBoYmFyIHBsb3Qgb2Ygc2FtcGxlIGNvdW50cyBieSByZWdpb24KcC5hbGwuaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5jb3VudC5hbGwsIGFlcyh4PWNvdW50LnBlci5yZWdpb24seT0iIikpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0ic3RhY2siLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdub25lJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz0iZ3JleTMwIikgKyAKICBnZW9tX3RleHQoZGF0YT1QSEUuY291bnQuYWxsLCBhZXMoKGNvdW50LnBlci5yZWdpb24rMTIpLCAiIixsYWJlbD1jb3VudC5wZXIucmVnaW9uKSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpICsKICBsYWJzKHk9IkFsbCIsIHg9IlNhbXBsZSBDb3VudCIpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKDAsMjYwKSkgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChucm93PTQpKSAKI3AuYWxsLmhiYXJwbG90CgojIG1ha2UgdGVtcG9yYWwgYnViYmxlcGxvdCBvZiBjb3VudHMgYnkgcmVnaW9uCnAuYWxsLnllYXIuYnViYmxlcGxvdCA8LSBnZ3Bsb3QoUEhFLmNvdW50LnllYXJzLCBhZXMoYXMubnVtZXJpYyh5ZWFyKSwgeT0iQWxsIikpICsKICBnZW9tX3BvaW50KGFscGhhPTAuNjUsIGFlcyhzaXplPWNvdW50LnBlci55ZWFyKSkgKyAKICBnZW9tX2xpbmUoYWxwaGE9MC4yNSkgKwogIGd1aWRlcyhjb2xvdXI9J25vbmUnKSArCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gNyxicmVha3M9YygxLDUsMTAsMjUsNTApKSArIAogIGd1aWRlcyhzaXplPWd1aWRlX2xlZ2VuZChucm93PTIpKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPSJncmV5MzAiKSArIAogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nbm9uZScpICsKICBsYWJzKHk9IiIsIHg9IlNhbXBsZSBZZWFyIiwgc2l6ZT0iQ291bnQiKSAKI3AuYWxsLnllYXIuYnViYmxlcGxvdAoKIyBNYWtlIHByb3BvcnRpb25hbCBoYmFyIHBsb3Qgb2YgSElWIHN0YXR1cwpwLmFsbC5oaXYuaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5ISVYuY291bnRzLCBhZXMoQ291bnQseT0iIixmaWxsPWhpdnBvcykpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J25vbmUnKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iSElWICt2ZSIsdmFsdWVzPVBIRS5oaXYuY29scyRoaXYuY29scywgYnJlYWtzPVBIRS5oaXYuY29scyRoaXZwb3MpICsKICBsYWJzKHk9IkFsbCIsIHg9IkhJViArdmUiKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9MykpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuSElWLmNvdW50cywgYWVzKGN1bV9mcmFjdC5taWQsIHk9IiIsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikgKwogIE5VTEwKI3AuYWxsLmhpdi5oYmFycGxvdAoKcC5hbGwub3JpZW50YXRpb24uaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5vcmllbnRhdGlvbi5jb3VudHMsIGFlcyhDb3VudCx5PSIiLGZpbGw9Z2VuZGVyX29yaWVudGF0aW9uKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nbm9uZScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJPcmllbnRhdGlvbiIsdmFsdWVzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uLmNvbHMsIGJyZWFrcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbikgKwogIGxhYnMoeT0iQWxsIiwgeD0iT3JpZW50YXRpb24iKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9MykpICsKICBnZW9tX3RleHQoZGF0YT1QSEUub3JpZW50YXRpb24uY291bnRzLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgeT0iIixsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQojcC5hbGwub3JpZW50YXRpb24uaGJhcnBsb3QKCnAuYWxsLnVrYm9ybi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLlVLYm9ybi5jb3VudHMsIGFlcyhDb3VudCx5PSIiLGZpbGw9dWtib3JuKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nbm9uZScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJVS1xuQm9ybiIsdmFsdWVzPVBIRS51a2Jvcm4uY29scyR1a2Jvcm4uY29scywgYnJlYWtzPVBIRS51a2Jvcm4uY29scyR1a2Jvcm4pICsKICBsYWJzKHk9IkFsbCIsIHg9IlVLIEJvcm4iKSArCiAgI2d1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChucm93PTMpKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLlVLYm9ybi5jb3VudHMsIGFlcyhjdW1fZnJhY3QubWlkLCB5PSIiLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpCiNwLmFsbC51a2Jvcm4uaGJhcnBsb3QKCnAuYWxsLkxvbmRvbi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLkxvbmRvbi5jb3VudHMsIGFlcyhDb3VudCx5PSIiLGZpbGw9bG9uZG9uKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nbm9uZScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJMb25kb24iLHZhbHVlcz1QSEUubG9uZG9uLmNvbHMkbG9uZG9uLmNvbHMsIGJyZWFrcz1QSEUubG9uZG9uLmNvbHMkbG9uZG9uKSArCiAgbGFicyh5PSJBbGwiLCB4PSJMb25kb24iKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9MykpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuTG9uZG9uLmNvdW50cywgYWVzKGN1bV9mcmFjdC5taWQsIHk9IiIsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikKI3AuYWxsLkxvbmRvbi5oYmFycGxvdAoKcC5hbGwuQWdlLmhiYXJwbG90IDwtIGdncGxvdChQSEUuQWdlLmNvdW50cywgYWVzKENvdW50LHk9IiIsZmlsbD1hZ2VfZ3JvdXApKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdub25lJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkFnZVxuR3JvdXAiLHZhbHVlcz1QSEUuQWdlLmNvbHMkYWdlX2dyb3VwLmNvbHMsIGJyZWFrcz1QSEUuQWdlLmNvbHMkYWdlX2dyb3VwKSArCiAgbGFicyh5PSJBbGwiLCB4PSJBZ2UgR3JvdXAiKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9MykpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuQWdlLmNvdW50cywgYWVzKGN1bV9mcmFjdC5taWQsIHk9IiIsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikKI3AuYWxsLkFnZS5oYmFycGxvdApgYGAKXApQbG90IGNvbWJpbmVkIHBsb3QgZm9yICdhbGwgc2FtcGxlcycKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMH0KUEhFLmFsbC5jb21iaXBsb3QuMSA8LSBwbG90X2dyaWQocC5hbGwueWVhci5idWJibGVwbG90LCBwLmFsbC5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuYWxsLm9yaWVudGF0aW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5hbGwuaGl2LmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5hbGwuQWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgbnJvdz0xLCBhbGlnbj0iaCIsIHJlbF93aWR0aHM9Yyg0LDIsMiwyLDIpLCBzY2FsZT0wLjkpCgpQSEUuYWxsLmNvbWJpcGxvdC4xCmBgYApcClwKTmV4dCBqdXN0IGRlc2NyaWJlIHBvcHVsYXRpb24gZGlzdHJpYnV0aW9ucyBieSBQSEUgcmVnaW9uCmBgYHtyfQojIGdlbmVyYXRlIHNvbWUgYmFzaWMgc3RhdGlzdGljcyBhYm91dCBnZW9ncmFwaGljYWwgUEhFIHJlZ2lvbnMgKGFub255bWlzZWQpClBIRS5nZW8uY291bnQgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHBoZV9jZW50cmUpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQucGVyLnJlZ2lvbj1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuY291bnQ9c3VtKGNvdW50LnBlci5yZWdpb24pLGZyYWN0aW9uPWNvdW50LnBlci5yZWdpb24vdG90YWwuY291bnQpCgpQSEUuZ2VvLmNvdW50LnllYXJzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShwaGVfY2VudHJlLHllYXIpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQucGVyLnJlZ2lvbi55ZWFyPW4oKSkKClBIRS5nZW8uY291bnQueWVhcnMubGluZWFnZSA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkocGhlX2NlbnRyZSx5ZWFyLFRQQV9MaW5lYWdlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50LnBlci5yZWdpb24ueWVhcj1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuY291bnQueWVhcj1zdW0oY291bnQucGVyLnJlZ2lvbi55ZWFyKSkgJT4lCiAgZHBseXI6OnVuZ3JvdXAoKSAlPiUKICB0aWR5cjo6cGl2b3Rfd2lkZXIobmFtZXNfZnJvbT1UUEFfTGluZWFnZSwgdmFsdWVzX2Zyb20gPSBjb3VudC5wZXIucmVnaW9uLnllYXIpClBIRS5nZW8uY291bnQueWVhcnMubGluZWFnZVtpcy5uYShQSEUuZ2VvLmNvdW50LnllYXJzLmxpbmVhZ2UpXSA8LSAwClBIRS5nZW8uY291bnQueWVhcnMubGluZWFnZSR5ZWFyIDwtIGFzLm51bWVyaWMoUEhFLmdlby5jb3VudC55ZWFycy5saW5lYWdlJHllYXIpCgojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgSElWIHN0YXR1cwpQSEUuZ2VvLkhJVi5jb3VudHMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHBoZV9jZW50cmUsaGl2cG9zKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50LnBlci5yZWdpb24uaGl2PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKGNvdW50LnBlci5yZWdpb24uaGl2KSkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1jb3VudC5wZXIucmVnaW9uLmhpdi90b3RhbC5yZWdpb24pICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoaGl2cG9zKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoY3VtX2ZyYWN0ID0gY3Vtc3VtKGZyYWN0aW9uKSkgJT4lCiAgZHBseXI6Om11dGF0ZShjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCiMgRG91YmxlIENoZWNrIEhJViBzdGF0dXMgZGF0YSBmb3Igbm9uLVBIRSBkYXRhc2V0IC0gY29uZmlybWVkIG5vIEhJVit2ZXMgZnJvbSBub24tTVNNLiAKUEhFLnNvdXJjZWxhYi5ISVYuY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShpcy5QSEUsIGdlbmRlcl9vcmllbnRhdGlvbiwgaGl2cG9zKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50LnBlci5vcmllbnRhdGlvbi5oaXY9bigpKSAjJT4lCiAgI2RwbHlyOjpmaWx0ZXIoaXMuUEhFIT0iUEhFIikKCiMgR2V0IHRvdGFsIHBvcHVsYXRpb24gc3RhdHMgZm9yIEhJVgpQSEUuYWxsLkhJVi5jb3VudHMgPC0gIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShoaXZwb3MpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQuaGl2PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZShjb3VudC50b3RhbD1zdW0oY291bnQuaGl2KSwgZnJhY3Rpb249Y291bnQuaGl2L2NvdW50LnRvdGFsKQoKICAKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IGdlbmRlciBvcmllbnRhdGlvbgpQSEUub3JpZW50YXRpb24uY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShnZW5kZXJfb3JpZW50YXRpb24pICU+JQogIGRwbHlyOjpzdW1tYXJpc2Uob3JpZW50YXRpb24uY291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKG9yaWVudGF0aW9uLnBlcmNlbnQ9KG9yaWVudGF0aW9uLmNvdW50L3N1bShvcmllbnRhdGlvbi5jb3VudCkqMTAwKSkKClBIRS5nZW8ub3JpZW50YXRpb24uY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShwaGVfY2VudHJlLGdlbmRlcl9vcmllbnRhdGlvbikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudC5wZXIucmVnaW9uLm9yaWVudGF0aW9uPW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKGNvdW50LnBlci5yZWdpb24ub3JpZW50YXRpb24pKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKGdlbmRlcl9vcmllbnRhdGlvbiksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPWNvdW50LnBlci5yZWdpb24ub3JpZW50YXRpb24vdG90YWwucmVnaW9uLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpICU+JSAKICBkcGx5cjo6bXV0YXRlKG9yaWVudGF0aW9uLnBlcmNlbnQ9KGNvdW50LnBlci5yZWdpb24ub3JpZW50YXRpb24vc3VtKGNvdW50LnBlci5yZWdpb24ub3JpZW50YXRpb24pKjEwMCkpCgojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgVUsgYm9ybgpQSEUuZ2VvLlVLYm9ybiA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkocGhlX2NlbnRyZSwgdWtib3JuKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyh1a2Jvcm4pLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKICAKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IExvbmRvbiBiYXNlZApQSEUuZ2VvLkxvbmRvbiA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkocGhlX2NlbnRyZSwgbG9uZG9uKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhsb25kb24pLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBBZ2UgZ3JvdXAKUEhFLmdlby5BZ2UgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHBoZV9jZW50cmUsIGFnZV9ncm91cCkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwucmVnaW9uPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoYWdlX2dyb3VwKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwucmVnaW9uLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpCgojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgTGluZWFnZSBncm91cApQSEUuZ2VvLkxpbmVhZ2UgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHBoZV9jZW50cmUsIFRQQV9MaW5lYWdlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhUUEFfTGluZWFnZSksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnJlZ2lvbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKQoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IHN1YmxpbmVhZ2UgZ3JvdXAKUEhFLmdlby5zdWJsaW5lYWdlIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShwaGVfY2VudHJlLCBUUEEucGluZWNvbmUuc3VibGluZWFnZSkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwucmVnaW9uPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKYGBgClwKTWFrZSBzb21lIHBsb3RzCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9OH0KIyBNYWtlIGhiYXIgcGxvdCBvZiBzYW1wbGUgY291bnRzIGJ5IHJlZ2lvbgpwLnJlZ2lvbi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLmdlby5jb3VudCwgYWVzKGNvdW50LnBlci5yZWdpb24scGhlX2NlbnRyZSwgZmlsbD1waGVfY2VudHJlKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJzdGFjayIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJVS0hTQVxuUmVnaW9uIix2YWx1ZXM9UEhFLnJlZ2lvbi5jb2xzLmJyZXckcmVnaW9uLmNvbCwgYnJlYWtzPVBIRS5yZWdpb24uY29scy5icmV3JFVLSFNBLnJlZ2lvbikgKwogIGdlb21fdGV4dChkYXRhPVBIRS5nZW8uY291bnQsIGFlcygoY291bnQucGVyLnJlZ2lvbisxMiksIHBoZV9jZW50cmUsbGFiZWw9Y291bnQucGVyLnJlZ2lvbiksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKSArCiAgbGFicyh5PSJVS0hTQSBSZWdpb24iLCB4PSJTYW1wbGUgQ291bnQiKSArCiAgI2Nvb3JkX2NhcnRlc2lhbih4bGltPWMoMCwxMzApKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW09YygwLDI2MCkpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobmNvbD0yKSkgCiNwLnJlZ2lvbi5oYmFycGxvdAoKIyBtYWtlIHRlbXBvcmFsIGJ1YmJsZXBsb3Qgb2YgY291bnRzIGJ5IHJlZ2lvbgpwLnJlZ2lvbi55ZWFyLmJ1YmJsZXBsb3QgPC0gZ2dwbG90KFBIRS5nZW8uY291bnQueWVhcnMsIGFlcyhhcy5udW1lcmljKHllYXIpLCBwaGVfY2VudHJlLCBjb2xvdXI9cGhlX2NlbnRyZSkpICsKICBnZW9tX3BvaW50KGFscGhhPTAuNjUsIGFlcyhzaXplPWNvdW50LnBlci5yZWdpb24ueWVhcikpICsgCiAgZ2VvbV9saW5lKGFscGhhPTAuMjUpICsKICBndWlkZXMoY29sb3VyPSdub25lJykgKwogIHNjYWxlX3NpemVfYXJlYShtYXhfc2l6ZSA9IDcsYnJlYWtzPWMoMSw1LDEwLDI1LDUwKSkgKyAKICBndWlkZXMoc2l6ZT1ndWlkZV9sZWdlbmQobnJvdz0yLCBkaXJlY3Rpb24gPSAnaG9yaXpvbnRhbCcsIGJ5cm93PVQpKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IlVLSFNBXG5SZWdpb24iLHZhbHVlcz1QSEUucmVnaW9uLmNvbHMuYnJldyRyZWdpb24uY29sLCBicmVha3M9UEhFLnJlZ2lvbi5jb2xzLmJyZXckVUtIU0EucmVnaW9uKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgbGFicyh5PSJVS0hTQSBSZWdpb24iLCB4PSJTYW1wbGUgWWVhciIsIHNpemU9IkNvdW50IikgCiNwLnJlZ2lvbi55ZWFyLmJ1YmJsZXBsb3QKCiMgT3IgYSBiYXJwbG90IG9mIGxpbmVhZ2UgYnkgeWVhciAmIFBIRSByZWdpb24/CnAucmVnaW9uLnllYXIuYnViYmxlcGxvdC5iYXJwbG90LmZhY2V0LmxpbmVhZ2UgPC0gUEhFLmdlby5jb3VudC55ZWFycy5saW5lYWdlICU+JSB0aWR5cjo6cGl2b3RfbG9uZ2VyKGMoU1MxNCwgTmljaG9scyksIG5hbWVzX3RvPSJUUEFfTGluZWFnZSIsIHZhbHVlc190bz0iQ291bnQiKSAlPiUKICBnZ3Bsb3QoYWVzKHllYXIsIENvdW50LCBmaWxsPVRQQV9MaW5lYWdlKSkgKyAKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsIHdpZHRoPTAuNikgKyAKICBmYWNldF9ncmlkKHBoZV9jZW50cmV+Liwgc2NhbGVzPSdmcmVlJykgKwogIGd1aWRlcyhzaXplPWd1aWRlX2xlZ2VuZChucm93PTIpKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iVFBBXG5MaW5lYWdlIix2YWx1ZXM9VFBBX0xpbmVhZ2UuY29scyRMaW5lYWdlLmNvbCwgYnJlYWtzPVRQQV9MaW5lYWdlLmNvbHMkTGluZWFnZSkgKwogIHRoZW1lKHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3I9J3doaXRlJywgZmlsbD0nd2hpdGUnLGxpbmV0eXBlPSJzb2xpZCIpLCBzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiZ3JleTI1Iiwgc2l6ZT03LCBhbmdsZT0wKSkgCiNwLnJlZ2lvbi55ZWFyLmJ1YmJsZXBsb3QuYmFycGxvdC5mYWNldC5saW5lYWdlCgojIE1ha2UgcHJvcG9ydGlvbmFsIGhiYXIgcGxvdCBvZiBISVYgc3RhdHVzCnAucmVnaW9uLmhpdi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLmdlby5ISVYuY291bnRzLCBhZXMoY291bnQucGVyLnJlZ2lvbi5oaXYscGhlX2NlbnRyZSxmaWxsPWhpdnBvcykpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJISVYgK3ZlIix2YWx1ZXM9UEhFLmhpdi5jb2xzJGhpdi5jb2xzLCBicmVha3M9UEhFLmhpdi5jb2xzJGhpdnBvcykgKwogIGxhYnMoeT0iVUtIU0EgUmVnaW9uIiwgeD0iSElWICt2ZSIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobnJvdz0zKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5nZW8uSElWLmNvdW50cywgYWVzKGN1bV9mcmFjdC5taWQsIHBoZV9jZW50cmUsbGFiZWw9Y291bnQucGVyLnJlZ2lvbi5oaXYpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikgKwogIE5VTEwKI3AucmVnaW9uLmhpdi5oYmFycGxvdAoKcC5yZWdpb24ub3JpZW50YXRpb24uaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5nZW8ub3JpZW50YXRpb24uY291bnRzLCBhZXMoY291bnQucGVyLnJlZ2lvbi5vcmllbnRhdGlvbixwaGVfY2VudHJlLGZpbGw9Z2VuZGVyX29yaWVudGF0aW9uKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9Ik9yaWVudGF0aW9uIix2YWx1ZXM9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24uY29scywgYnJlYWtzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uKSArCiAgbGFicyh5PSJVS0hTQSBSZWdpb24iLCB4PSJPcmllbnRhdGlvbiIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobmNvbD0xKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5nZW8ub3JpZW50YXRpb24uY291bnRzLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgcGhlX2NlbnRyZSxsYWJlbD1jb3VudC5wZXIucmVnaW9uLm9yaWVudGF0aW9uKSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpCiNwLnJlZ2lvbi5vcmllbnRhdGlvbi5oYmFycGxvdAoKcC5yZWdpb24udWtib3JuLmhiYXJwbG90IDwtIGdncGxvdChQSEUuZ2VvLlVLYm9ybiwgYWVzKENvdW50LHBoZV9jZW50cmUsZmlsbD11a2Jvcm4pKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iVUsgQm9ybiIsdmFsdWVzPVBIRS51a2Jvcm4uY29scyR1a2Jvcm4uY29scywgYnJlYWtzPVBIRS51a2Jvcm4uY29scyR1a2Jvcm4pICsKICBsYWJzKHk9IlVLSFNBIFJlZ2lvbiIsIHg9IlVLIEJvcm4iKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9MykpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuZ2VvLlVLYm9ybiwgYWVzKGN1bV9mcmFjdC5taWQsIHBoZV9jZW50cmUsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikKI3AucmVnaW9uLnVrYm9ybi5oYmFycGxvdAoKcC5yZWdpb24uTG9uZG9uLmhiYXJwbG90IDwtIGdncGxvdChQSEUuZ2VvLkxvbmRvbiwgYWVzKENvdW50LHBoZV9jZW50cmUsZmlsbD1sb25kb24pKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iTG9uZG9uIix2YWx1ZXM9UEhFLmxvbmRvbi5jb2xzJGxvbmRvbi5jb2xzLCBicmVha3M9UEhFLmxvbmRvbi5jb2xzJGxvbmRvbikgKwogIGxhYnMoeT0iVUtIU0EgUmVnaW9uIiwgeD0iTG9uZG9uIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChucm93PTMpKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLmdlby5Mb25kb24sIGFlcyhjdW1fZnJhY3QubWlkLCBwaGVfY2VudHJlLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpCiNwLnJlZ2lvbi5Mb25kb24uaGJhcnBsb3QKCnAucmVnaW9uLkFnZS5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLmdlby5BZ2UsIGFlcyhDb3VudCxwaGVfY2VudHJlLGZpbGw9YWdlX2dyb3VwKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkFnZVxuR3JvdXAiLHZhbHVlcz1QSEUuQWdlLmNvbHMkYWdlX2dyb3VwLmNvbHMsIGJyZWFrcz1QSEUuQWdlLmNvbHMkYWdlX2dyb3VwKSArCiAgbGFicyh5PSJVS0hTQSBSZWdpb24iLCB4PSJBZ2UgR3JvdXAiKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5jb2w9MSkpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuZ2VvLkFnZSwgYWVzKGN1bV9mcmFjdC5taWQsIHBoZV9jZW50cmUsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikKI3AucmVnaW9uLkFnZS5oYmFycGxvdApgYGAKXApDb21iaW5lZCBwbG90CmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTB9ClBIRS5yZWdpb24uY29tYmlwbG90LjEgPC0gcGxvdF9ncmlkKHAucmVnaW9uLnllYXIuYnViYmxlcGxvdCwgcC5yZWdpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnJlZ2lvbi5vcmllbnRhdGlvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAucmVnaW9uLmhpdi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAucmVnaW9uLkFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIG5yb3c9MSwgYWxpZ249ImgiLCByZWxfd2lkdGhzPWMoNCwyLDIsMiwyKSwgc2NhbGU9MC45KQoKUEhFLnJlZ2lvbi5jb21iaXBsb3QuMQpgYGAKCgpcClJlZ2lvbnMgYXMgYSBjb21wbGV4IG11bHRpcGFuZWwgcGxvdApgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTQuNX0KCgojIGxlZ2VuZHMKUEhFLnJlZ2lvbi5jb21iaXBsb3QuMS5sZWdlbmRzIDwtIHBsb3RfZ3JpZChnZXRfbGVnZW5kKHAucmVnaW9uLnllYXIuYnViYmxlcGxvdCksIGdldF9sZWdlbmQocC5yZWdpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwKSwgZ2V0X2xlZ2VuZChwLnJlZ2lvbi5vcmllbnRhdGlvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXApLCBnZXRfbGVnZW5kKHAucmVnaW9uLmhpdi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXApLCBnZXRfbGVnZW5kKHAucmVnaW9uLkFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXApLCBucm93PTEsIGFsaWduPSJoIiwgcmVsX3dpZHRocz1jKDYsNCw0LDQsNCksIHNjYWxlPTAuOTUpCgoKIyBBcnJhbmdlIHBsb3RzIHZlcnRpY2FsbHkKcC55ZWFyLmJ1YmJsZXBsb3QuY29tYmkgPC0gcGxvdF9ncmlkKHAuYWxsLnllYXIuYnViYmxlcGxvdCArIHgudGhlbWUuc3RyaXAsIHAucmVnaW9uLnllYXIuYnViYmxlcGxvdCArIGxlZ2VuZC5zdHJpcCwgbmNvbD0xLCBhbGlnbj0idiIsYXhpcz0ibHIiLCByZWxfaGVpZ2h0cz1jKDEsNykpCgpwLnJlZ2lvbi5oYmFyLmNvdW50cy5jb21iaSA8LSBwbG90X2dyaWQocC5hbGwuaGJhcnBsb3QgKyB4LnRoZW1lLnN0cmlwICsgeS50aGVtZS5zdHJpcCwgcC5yZWdpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICsgbGVnZW5kLnN0cmlwLCBuY29sPTEsIGFsaWduPSJ2IixheGlzPSJsciIsIHJlbF9oZWlnaHRzPWMoMSw3KSkKCnAucmVnaW9uLmhiYXIub3JpZW50YXRpb24uY29tYmkgPC0gcGxvdF9ncmlkKHAuYWxsLm9yaWVudGF0aW9uLmhiYXJwbG90ICsgeC50aGVtZS5zdHJpcCArIHkudGhlbWUuc3RyaXAsIHAucmVnaW9uLm9yaWVudGF0aW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCArIGxlZ2VuZC5zdHJpcCwgbmNvbD0xLCBhbGlnbj0idiIsYXhpcz0ibHIiLCByZWxfaGVpZ2h0cz1jKDEsNykpCgpwLnJlZ2lvbi5oYmFyLmhpdi5jb21iaSA8LSBwbG90X2dyaWQocC5hbGwuaGl2LmhiYXJwbG90ICsgeC50aGVtZS5zdHJpcCArIHkudGhlbWUuc3RyaXAsIHAucmVnaW9uLmhpdi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIG5jb2w9MSwgYWxpZ249InYiLGF4aXM9ImxyIiwgcmVsX2hlaWdodHM9YygxLDcpKQoKcC5yZWdpb24uaGJhci5BZ2UuY29tYmkgPC0gcGxvdF9ncmlkKHAuYWxsLkFnZS5oYmFycGxvdCArIHgudGhlbWUuc3RyaXAgKyB5LnRoZW1lLnN0cmlwLCBwLnJlZ2lvbi5BZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICsgbGVnZW5kLnN0cmlwLCBuY29sPTEsIGFsaWduPSJ2IixheGlzPSJsciIsIHJlbF9oZWlnaHRzPWMoMSw3KSkKCiMgQ29tYmluZSB0aGUgcGxvdHMKcC5yZWdpb24uaGJhci5jb21iaS5wbHVzLmFsbCA8LSBwbG90X2dyaWQocC55ZWFyLmJ1YmJsZXBsb3QuY29tYmksIHAucmVnaW9uLmhiYXIuY291bnRzLmNvbWJpLCBwLnJlZ2lvbi5oYmFyLm9yaWVudGF0aW9uLmNvbWJpLCBwLnJlZ2lvbi5oYmFyLmhpdi5jb21iaSwgcC5yZWdpb24uaGJhci5BZ2UuY29tYmksIG5yb3c9MSwgcmVsX3dpZHRocz1jKDYsNCw0LDQsNCksIGxhYmVscyA9IGMoIkEiLCJCIiwiQyIsIkQiLCJFIiksIGxhYmVsX3NpemU9cGFuZWwubGFiLnNpemUsIHZqdXN0PTAuMjUpCiMgYW5kIGFkZCB0aGUgbGVnZW5kcyBvbiB0b3AKcC5yZWdpb24uaGJhci5jb21iaS5wbHVzLmFsbC53aXRoLmxlZ2VuZHMgPC0gcGxvdF9ncmlkKHAucmVnaW9uLmhiYXIuY29tYmkucGx1cy5hbGwsIFBIRS5yZWdpb24uY29tYmlwbG90LjEubGVnZW5kcywgbmNvbD0xLCByZWxfaGVpZ2h0cz1jKDYsMSksIHNjYWxlID0gMC45NSkKCgoKcC5yZWdpb24uaGJhci5jb21iaS5wbHVzLmFsbC53aXRoLmxlZ2VuZHMKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksICJTdXBGaWcyX1RQQS1QSEVfU2FtcGxlLW1ldGFkaXN0cm9zLWJ5LXBoZV9yZWdpb24rYWxsLWNvbWJpLiIsZm9ybWF0KFN5cy5EYXRlKCksIiVZJW0lZCIpLCIucGRmIiksIHVuaXRzPSdtbScsIHdpZHRoPTI0MCwgaGVpZ2h0PTEzNSwgZGV2aWNlPSdwZGYnLCBkcGk9MTIwMCkKCmBgYApcClwKTm93IGxldHMgbG9vayBhdCBzb21lIGdlbmV0aWMgZGF0YQpcCiMjIyBNYWtlIE1MIHRyZWUgd2l0aCBzdWJsaW5lYWdlIHRpcHBvaW50cwpgYGB7cn0KVFBBLk1MdHJlZS5nZ3RyZWUudGlwcG9pbnQgPC0gVFBBLk1MdHJlZS5nZ3RyZWUgJTwrJSBkYXRhLmZyYW1lKFNhbXBsZV9OYW1lPVRQQS5tZXRhMi4xJFNhbXBsZV9OYW1lLCBTdWJsaW5lYWdlPVRQQS5tZXRhMi4xJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSArIAogIGdlb21fdGlwcG9pbnQoYWVzKGNvbG9yPVN1YmxpbmVhZ2UpLCBzaXplPTAuNSwgYWxwaGE9MC41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSJTdWJsaW5lYWdlIix2YWx1ZXM9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UuY29scywgYnJlYWtzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlKQpgYGAKXApBZGQgbWV0YWRhdGEKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMH0KIyBDb250aW5lbnQKcC5UUEEuTUx0cmVlLlBIRSA8LSBnaGVhdG1hcChUUEEuTUx0cmVlLmdndHJlZS50aXBwb2ludCwKICAgICAgICAgICAgICAgVFBBLnJhd3NlcS5jb250aW5lbnRzLnAsIGNvbG9yPU5VTEwsd2lkdGg9MC4wNzUsb2Zmc2V0PTAuMDAwMDAwMjUsIGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLGZvbnQuc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkNvbnRpbmVudCIsdmFsdWVzPWNvbnRpbmVudGFsLmNvbHMuYnJldzIkY29udGluZW50LmNvbCwgYnJlYWtzPWNvbnRpbmVudGFsLmNvbHMuYnJldzIkQ29udGluZW50LCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEsbmNvbD0yKSkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0ncmlnaHQnKSArCiAgbmV3X3NjYWxlX2ZpbGwoKQoKIyBpcyBVSwpwLlRQQS5NTHRyZWUuUEhFIDwtIGdoZWF0bWFwKHAuVFBBLk1MdHJlZS5QSEUsCiAgICAgICAgICAgICAgIFRQQS5yYXdzZXEuVUsucCwgY29sb3I9TlVMTCx3aWR0aD0wLjA3NSxvZmZzZXQ9MC4wMDAwMTAyNSwgY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsgCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iRW5nbGFuZC9PdGhlciIsIHZhbHVlcz1jKCJibGFjayIsImdyZXk5NSIpLCBicmVha3M9YygiRW5nbGFuZCIsIk90aGVyIiksIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMixuY29sPTIpKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICBuZXdfc2NhbGVfZmlsbCgpCgojIExpbmVhZ2UKcC5UUEEuTUx0cmVlLlBIRSA8LSBnaGVhdG1hcChwLlRQQS5NTHRyZWUuUEhFLFRQQS5yYXdzZXEuTGluZWFnZS5wLCBjb2xvcj1OVUxMLHdpZHRoPTAuMDc1LG9mZnNldD0wLjAwMDAyMDI1LCBjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCxmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJMaW5lYWdlIix2YWx1ZXM9VFBBX0xpbmVhZ2UuY29scyRMaW5lYWdlLmNvbCwgYnJlYWtzPVRQQV9MaW5lYWdlLmNvbHMkTGluZWFnZSwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAzLCBuY29sPTIpKSArIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0ncmlnaHQnKSArCiAgIG5ld19zY2FsZV9maWxsKCkgKwogIE5VTEwKCiMgc3VibGluZWFnZQpwLlRQQS5NTHRyZWUuUEhFIDwtIGdoZWF0bWFwKHAuVFBBLk1MdHJlZS5QSEUsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVRQQS5tZXRhMi4xJFNhbXBsZV9OYW1lLCBTdWJsaW5lYWdlPVRQQS5tZXRhMi4xJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSwgY29sb3I9TlVMTCx3aWR0aD0wLjA3NSxvZmZzZXQ9MC4wMDAwMzAyNSwgY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsgCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iU3VibGluZWFnZSIsdmFsdWVzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLmNvbHMsIGJyZWFrcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSA0LCBuY29sPTMpKSArIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0ncmlnaHQnKSArCiAgIG5ld19zY2FsZV9maWxsKCkgKwogIE5VTEwKYGBgClwKcGxvdApgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQpwLlRQQS5NTHRyZWUuUEhFCgojZ2dzYXZlKHBhc3RlMChGaWd1cmVfb3V0cHV0X2RpcmVjdG9yeSwgIlN1cEZpZzNfVFBBLVBIRV9HbG9iYWxfUGh5bG8rVUstaGlnaGxpZ2h0cy4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xODUsIGhlaWdodD0xNjAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCmBgYApcClwKIyMjIEdlb2dyYXBoaWMgZGlzdHJpYnV0aW9ucyBvZiBMaW5lYWdlcyBhbmQgU3VibGluZWFnZXMKV2hhdCBhYm91dCBzdWJsaW5lYWdlcz8KYGBge3J9CnAucmVnaW9uLkxpbmVhZ2UuaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5nZW8uTGluZWFnZSwgYWVzKENvdW50LHBoZV9jZW50cmUsZmlsbD1UUEFfTGluZWFnZSkpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJUUEFcbkxpbmVhZ2UiLHZhbHVlcz1UUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UuY29sLCBicmVha3M9VFBBX0xpbmVhZ2UuY29scyRMaW5lYWdlKSArCiAgbGFicyh5PSJVS0hTQSBSZWdpb24iLCB4PSJUUEEgTGluZWFnZSIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobnJvdz0zKSkgKwogICNnZW9tX3RleHQoZGF0YT1QSEUuZ2VvLkxpbmVhZ2UsIGFlcyhjdW1fZnJhY3QubWlkLCBwaGVfY2VudHJlLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpICsKICBOVUxMCgpwLnJlZ2lvbi5zdWJsaW5lYWdlLmhiYXJwbG90IDwtIGdncGxvdChQSEUuZ2VvLnN1YmxpbmVhZ2UsIGFlcyhDb3VudCxwaGVfY2VudHJlLGZpbGw9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iVFBBXG5TdWJsaW5lYWdlIix2YWx1ZXM9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UuY29scywgYnJlYWtzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlKSArCiAgbGFicyh5PSJVS0hTQSBSZWdpb24iLCB4PSJUUEEgU3VibGluZWFnZSIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobnJvdz00KSkgKwogICNnZW9tX3RleHQoZGF0YT1QSEUuZ2VvLnN1YmxpbmVhZ2UsIGFlcyhjdW1fZnJhY3QubWlkLCBwaGVfY2VudHJlLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpICsKICBOVUxMCgpgYGAKXApDb21iaSBwbG90IChnZW9ncmFwaHkgbGluZWFnZXMpCmBgYHtyfQpQSEUucmVnaW9uLmNvbWJpcGxvdC4yLmxpbmVhZ2VzIDwtIHBsb3RfZ3JpZChwLnJlZ2lvbi55ZWFyLmJ1YmJsZXBsb3QgK2xlZ2VuZC5zdHJpcCwgcC5yZWdpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICsgbGVnZW5kLnN0cmlwICsgY29vcmRfY2FydGVzaWFuKHhsaW09YygwLDE1MCkpLCBwLnJlZ2lvbi5MaW5lYWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCArbGVnZW5kLnN0cmlwLCBwLnJlZ2lvbi5zdWJsaW5lYWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCArbGVnZW5kLnN0cmlwLCBucm93PTEsIGFsaWduPSJoIiwgcmVsX3dpZHRocz1jKDYsMyw0LDQpLCBzY2FsZT0wLjk5LCBsYWJlbHM9YygiQyIsIkQiLCJFIiwiRiIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplKQoKIyBzZXBhcmF0ZSBvdXQgdGhlIHBsb3QgZm9yIHRoZSBsZWdlbmRzCnAucmVnaW9uLnllYXIuYnViYmxlcGxvdC5sZWdlbmQgPC0gZ2V0X2xlZ2VuZChwLnJlZ2lvbi55ZWFyLmJ1YmJsZXBsb3QpCnAucmVnaW9uLmhiYXJwbG90LmxlZ2VuZCA8LSBnZXRfbGVnZW5kKHAucmVnaW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCkKcC5yZWdpb24uTGluZWFnZS5oYmFycGxvdC5sZWdlbmQgPC0gZ2V0X2xlZ2VuZChwLnJlZ2lvbi5MaW5lYWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCkKcC5yZWdpb24uc3VibGluZWFnZS5oYmFycGxvdC5sZWdlbmQgPC0gZ2V0X2xlZ2VuZChwLnJlZ2lvbi5zdWJsaW5lYWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCkKClBIRS5yZWdpb24uY29tYmlwbG90LjIubGluZWFnZXMubGVnZW5kIDwtIHBsb3RfZ3JpZChwLnJlZ2lvbi55ZWFyLmJ1YmJsZXBsb3QubGVnZW5kLCBwLnJlZ2lvbi5oYmFycGxvdC5sZWdlbmQsIHAucmVnaW9uLkxpbmVhZ2UuaGJhcnBsb3QubGVnZW5kLCBwLnJlZ2lvbi5zdWJsaW5lYWdlLmhiYXJwbG90LmxlZ2VuZCwgbnJvdz0xLCBhbGlnbj0iaCIsIHJlbF93aWR0aHM9Yyg2LDMsNCw0KSkKClBIRS5yZWdpb24uY29tYmlwbG90LjIubGluZWFnZXMgPC0gcGxvdF9ncmlkKFBIRS5yZWdpb24uY29tYmlwbG90LjIubGluZWFnZXMsIFBIRS5yZWdpb24uY29tYmlwbG90LjIubGluZWFnZXMubGVnZW5kLCByZWxfaGVpZ2h0cyA9IGMoNCwxKSwgbmNvbD0xKQoKUEhFLnJlZ2lvbi5jb21iaXBsb3QuMi5saW5lYWdlcwpgYGAKXApPSywgbGV0J3Mgbm93IGFkZCBhIG1hcCBvZiB0aGVzZSBnZW9ncmFwaGljYWwgZGlzdHJpYnV0aW9ucwoKXApMZXQncyB1c2VkIE9OUyBwdWJsaXNoZWQgc2hhcGUgZmlsZXMgLSB0aGVyZSBpcyBvbmUgYXZhaWxhYmxlIHRoYXQgc2hvd3MgUHVibGljIEhlYWx0aCBFbmdsYW5kIHJlZ2lvbiBib3VuZGFyaWVzLiAKYGBge3J9CgojIEdlbmVyYXRlIGFwcHJveGltYXRlIHJlZ2lvbmFsIEdQUyBjb29yZHMKUEhFLnJlZ2lvbi5HUFMgPC0gZGF0YS5mcmFtZSgKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsCiAgICAgICAgICBwaGVfY2VudHJlID0gYygiRWFzdCBNaWRsYW5kcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiRWFzdCBvZiBFbmdsYW5kIiwiTG9uZG9uIiwiTm9ydGggRWFzdCIsIk5vcnRoIFdlc3QiLAogICAgICAgICAgICAgICAgICAgICAgICAgIlNvdXRoIEVhc3QiLCJTb3V0aCBXZXN0IiwiV2VzdCBNaWRsYW5kcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiWW9ya3NoaXJlIGFuZCBIdW1iZXIiLCJVSyAobm90IEVuZ2xhbmQpIiwiTm90IEtub3duIiksCiAgICAgICAgICAgIExvbmdpdHVkZSA9IGMoLTAuNywwLjUsLTAuMiwtMS45LC0yLjQsCiAgICAgICAgICAgICAgICAgICAgICAgICAwLjA1LC0yLjksLTIsLTAuOCwwLjEsMC42MyksCiAgICAgICAgICAgTGF0aXR1ZGUgPSBjKDUyLjksNTIuNCw1MS41LDU1LDUzLjcsCiAgICAgICAgICAgICAgICAgICAgICAgICA1MS4xLDUxLDUyLjYsNTMuOCw1NC43LDU0LjEpCiAgKSAgClBIRS5yZWdpb24uR1BTIDwtIGxlZnRfam9pbihQSEUucmVnaW9uLkdQUywgUEhFLmdlby5MaW5lYWdlW1BIRS5nZW8uTGluZWFnZSRUUEFfTGluZWFnZT09IlNTMTQiLGMoInBoZV9jZW50cmUiLCJDb3VudCIpXSwgYnk9InBoZV9jZW50cmUiKQpjb2xuYW1lcyhQSEUucmVnaW9uLkdQUylbNF0gPC0gIlNTMTQiClBIRS5yZWdpb24uR1BTIDwtIGxlZnRfam9pbihQSEUucmVnaW9uLkdQUywgUEhFLmdlby5MaW5lYWdlW1BIRS5nZW8uTGluZWFnZSRUUEFfTGluZWFnZT09Ik5pY2hvbHMiLGMoInBoZV9jZW50cmUiLCJDb3VudCIpXSwgYnk9InBoZV9jZW50cmUiKQpjb2xuYW1lcyhQSEUucmVnaW9uLkdQUylbNV0gPC0gIk5pY2hvbHMiClBIRS5yZWdpb24uR1BTW2lzLm5hKFBIRS5yZWdpb24uR1BTKV0gPC0gMAoKUEhFLnJlZ2lvbi5HUFMgPC0gbGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLCBQSEUuZ2VvLkxpbmVhZ2VbUEhFLmdlby5MaW5lYWdlJFRQQV9MaW5lYWdlPT0iU1MxNCIsYygicGhlX2NlbnRyZSIsInRvdGFsLnJlZ2lvbiIpXSwgYnk9InBoZV9jZW50cmUiKQpjb2xuYW1lcyhQSEUucmVnaW9uLkdQUylbNl0gPC0gIlJlZ2lvbl9Db3VudCIKClBIRS5yZWdpb24uR1BTJHJhZGl1cyA8LSAwLjUqKDEtMS9zcXJ0KFBIRS5yZWdpb24uR1BTJFJlZ2lvbl9Db3VudCkpCgoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIEltcG9ydCBkYXRhZmlsZSBmcm9tIGh0dHBzOi8vZ2VvcG9ydGFsLnN0YXRpc3RpY3MuZ292LnVrL2RhdGFzZXRzL3B1YmxpYy1oZWFsdGgtZW5nbGFuZC1jZW50cmVzLWRlY2VtYmVyLTIwMTYtZnVsbC1jbGlwcGVkLWJvdW5kYXJpZXMtaW4tZW5nbGFuZC9leHBsb3JlP2xvY2F0aW9uPTUyLjk1MDAwMCUyQy0yLjAwMDAwMCUyQzYuODgKClVLLnNoYXBlZmlsZSA8LSByZWFkT0dSKGRzbj1VSy5wdWJsaWNoZWFsdGguc2hhcGVmaWxlLmRhdGEpCgojUmVzaGFwZSBmb3IgZ2dwbG90MiB1c2luZyB0aGUgQnJvb20gcGFja2FnZQpVSy5tYXBkYXRhIDwtIHRpZHkoVUsuc2hhcGVmaWxlLCByZWdpb249InBoZWMxNm5tIikKCiNVSy5nZyA8LSBnZ3Bsb3QoKSArIGdlb21fcG9seWdvbihkYXRhID0gVUsubWFwZGF0YSwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSwgY29sb3IgPSAiI0ZGRkZGRiIsIHNpemUgPSAwLjI1KQpVSy5nZyA8LSBnZ3Bsb3QoKSArIGdlb21fcG9seWdvbihkYXRhID0gVUsubWFwZGF0YSwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSwgY29sb3I9ImdyZXkyNSIsIGZpbGw9ImdyZXk5MCIsIHNpemUgPSAwLjA3NSkKCiNVSy5nZyA8LSBVSy5nZyArIGNvb3JkX2ZpeGVkKDEpICsgdGhlbWVfbm90aGluZygpCiNVSy5nZwojIE1hcCBwbG90dGluZyBmaWxlIGJlY29tZXMgX3ZlcnlfIGJpZyAtIHVzZSBnZ3Jhc3RyIHRvIHJlZHVjZSB0aGUgc2l6ZQpVSy5nZyA8LWdncGxvdCgpICsgZ2dyYXN0cjo6cmFzdGVyaXNlKGdlb21fcG9seWdvbihkYXRhID0gVUsubWFwZGF0YSwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSwgY29sb3I9ImdyZXkyNSIsIGZpbGw9ImdyZXk5MCIsIHNpemUgPSAwLjA3NSksIGRwaT00MDApICsgY29vcmRfZml4ZWQoMSkgKyB0aGVtZV9ub3RoaW5nKCkKCiNyYXN0ZXJpc2UoZ2VvbV9wb2ludChhZXMoY2FyYXQsIHByaWNlLCBjb2xvdXIgPSBjdXQpLCBkYXRhPWRpYW1vbmRzKSwgZHBpPTMwKQoKCgojIENvbnZlcnQgVUsgcmVnaW9ucyB0byBiZSBjb21wYXRpYmxlIHdpdGggbWFwCiMgRmlyc3QgZmluZCBjZW50cmUgcG9pbnQgZm9yIGVhY2ggcmVnaW9uClVLLm1hcGRhdGEucmVnaW9ucy5tZWFuY29vcmRzIDwtIFVLLm1hcGRhdGEgJT4lIGRwbHlyOjpncm91cF9ieShpZCkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShtZWFuLmxhdD1tZWFuKGxhdCksIG1lYW4ubG9uZz1tZWRpYW4obG9uZykpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkKY29sbmFtZXMoVUsubWFwZGF0YS5yZWdpb25zLm1lYW5jb29yZHMpWzFdIDwtICJwaGVfY2VudHJlIgoKUEhFLnJlZ2lvbi5HUFMudWttYXAgPC0gZHBseXI6OmxlZnRfam9pbihQSEUucmVnaW9uLkdQUywgVUsubWFwZGF0YS5yZWdpb25zLm1lYW5jb29yZHMsIGJ5PSJwaGVfY2VudHJlIikKCiMgQWRkIGFydGlmaWNpYWwgbG9jYXRpb24gZm9yICdub3Qga25vd24nClBIRS5yZWdpb24uR1BTLnVrbWFwW1BIRS5yZWdpb24uR1BTLnVrbWFwJHBoZV9jZW50cmU9PSJOb3QgS25vd24iLCJtZWFuLmxhdCJdIDwtIDYwMDAwMApQSEUucmVnaW9uLkdQUy51a21hcFtQSEUucmVnaW9uLkdQUy51a21hcCRwaGVfY2VudHJlPT0iTm90IEtub3duIiwibWVhbi5sb25nIl0gPC0gNTUwMDAwCgojIFNoaWZ0ICJTb3V0aCBFYXN0IiBzbGlnaHRseSB0byByZWR1Y2UgdGhlIG92ZXJsYXAgd2l0aCBMb25kb24KUEhFLnJlZ2lvbi5HUFMudWttYXBbUEhFLnJlZ2lvbi5HUFMudWttYXAkcGhlX2NlbnRyZT09IlNvdXRoIEVhc3QiLCJtZWFuLmxvbmciXSA8LSA0NzUwMDAKIyBTaGlmdCAiRWFzdCBvZiBFbmdsYW5kIEVhc3QiIHNsaWdodGx5IHRvIHJlZHVjZSB0aGUgb3ZlcmxhcCB3aXRoIExvbmRvbiAKUEhFLnJlZ2lvbi5HUFMudWttYXBbUEhFLnJlZ2lvbi5HUFMudWttYXAkcGhlX2NlbnRyZT09IkVhc3Qgb2YgRW5nbGFuZCIsIm1lYW4ubGF0Il0gPC0gMjc1MDAwCgojIE5vdCBnb2luZyB0byB0cnkgcGxvdHRpbmcgdGhlIDIgc2FtcGxlcyBmcm9tIGVsc2V3aGVyZSBpbiB0aGUgVUssIHNvIHJlbW92ZSB0aGF0IHJvdwpQSEUucmVnaW9uLkdQUy51a21hcCA8LSBQSEUucmVnaW9uLkdQUy51a21hcFtQSEUucmVnaW9uLkdQUy51a21hcCRwaGVfY2VudHJlICE9ICJVSyAobm90IEVuZ2xhbmQpIixdCgojIENyZWF0ZSByYWRpdXMgdmFyaWFibGUgZm9yIHBsb3R0aW5nIHBpZSBzaXplcyAodXNlIGxvZzEwKG4pKjIwLDAwMCkKUEhFLnJlZ2lvbi5HUFMudWttYXAkcmFkaXVzLlVLIDwtIGxvZzEwKFBIRS5yZWdpb24uR1BTLnVrbWFwJFJlZ2lvbl9Db3VudCkqMjAwMDAKCiNQSEUuZ2VvLmNvdW50LnllYXJzLmxpbmVhZ2UKClVLLmdnLnNjYXR0ZXJwaWUgPC0gVUsuZ2cgKyBnZW9tX3NjYXR0ZXJwaWUoZGF0YT1QSEUucmVnaW9uLkdQUy51a21hcCwgYWVzKG1lYW4ubG9uZywgbWVhbi5sYXQsIGdyb3VwPXBoZV9jZW50cmUsIHI9cmFkaXVzLlVLKSwgYWxwaGE9MC44NSwgY29sb3I9TkEsIGNvbHM9YygiTmljaG9scyIsIlNTMTQiKSkgKyAKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJUUEFcbkxpbmVhZ2UiLHZhbHVlcz1UUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UuY29sLCBicmVha3M9VFBBX0xpbmVhZ2UuY29scyRMaW5lYWdlKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0idG9wIikKClVLLmdnLnNjYXR0ZXJwaWUgPC0gVUsuZ2cuc2NhdHRlcnBpZSArIGdlb21fc2NhdHRlcnBpZV9sZWdlbmQoUEhFLnJlZ2lvbi5HUFMudWttYXBbIWlzLm5hKFBIRS5yZWdpb24uR1BTLnVrbWFwJG1lYW4ubGF0KSwicmFkaXVzLlVLIl0sIGxhYmVsbGVyPWZ1bmN0aW9uKHgpIHJvdW5kKCgxMF4oeC8yMDAwMCkpLDApLCBuPTMsIHg9MTUwMDAwLCB5PTUwMDAwMCkKClVLLmdnLnNjYXR0ZXJwaWUgPC0gVUsuZ2cuc2NhdHRlcnBpZSArIHRoZW1lX25vdGhpbmcoKQoKIz8gQWRkIGxhYmVscwpVSy5nZy5zY2F0dGVycGllLmxhYnMgPC0gVUsuZ2cuc2NhdHRlcnBpZSArIGdlb21fbGFiZWxfcmVwZWwoZGF0YT1QSEUucmVnaW9uLkdQUy51a21hcFshaXMubmEoUEhFLnJlZ2lvbi5HUFMudWttYXAkbWVhbi5sYXQpLF0sIGFlcyhtZWFuLmxvbmcsIG1lYW4ubGF0LCBsYWJlbD1waGVfY2VudHJlKSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBudWRnZV94ID0gNTAwMDAsIG51ZGdlX3kgPSAtMjUwMDAsIHNlZ21lbnQuc2l6ZSAgPSAwLjEpICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksIGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikgKyAKICB0aGVtZS50ZXh0LnNpemUgKwogIHRoZW1lX25vdGhpbmcoKQoKVUsuZ2cuc2NhdHRlcnBpZS5sYWJzCmBgYApcClwKTm93IGRvIGFuIGVxdWl2YWxlbnQgcGxvdCBmb3Igc3VibGluZWFnZXMKYGBge3J9ClBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiA8LSBQSEUucmVnaW9uLkdQUy51a21hcAoKClBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiA8LSBsZWZ0X2pvaW4oUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluLCBQSEUuZ2VvLnN1YmxpbmVhZ2VbUEhFLmdlby5zdWJsaW5lYWdlJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlPT0iMSIsYygicGhlX2NlbnRyZSIsIkNvdW50IildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbilbMTFdIDwtICIxIgpQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4gPC0gbGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiwgUEhFLmdlby5zdWJsaW5lYWdlW1BIRS5nZW8uc3VibGluZWFnZSRUUEEucGluZWNvbmUuc3VibGluZWFnZT09IjIiLGMoInBoZV9jZW50cmUiLCJDb3VudCIpXSwgYnk9InBoZV9jZW50cmUiKQpjb2xuYW1lcyhQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4pWzEyXSA8LSAiMiIKUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluIDwtIGxlZnRfam9pbihQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4sIFBIRS5nZW8uc3VibGluZWFnZVtQSEUuZ2VvLnN1YmxpbmVhZ2UkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2U9PSIzIixjKCJwaGVfY2VudHJlIiwiQ291bnQiKV0sIGJ5PSJwaGVfY2VudHJlIikKY29sbmFtZXMoUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluKVsxM10gPC0gIjMiClBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiA8LSBsZWZ0X2pvaW4oUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluLCBQSEUuZ2VvLnN1YmxpbmVhZ2VbUEhFLmdlby5zdWJsaW5lYWdlJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlPT0iNiIsYygicGhlX2NlbnRyZSIsIkNvdW50IildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbilbMTRdIDwtICI2IgpQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4gPC0gbGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiwgUEhFLmdlby5zdWJsaW5lYWdlW1BIRS5nZW8uc3VibGluZWFnZSRUUEEucGluZWNvbmUuc3VibGluZWFnZT09IjgiLGMoInBoZV9jZW50cmUiLCJDb3VudCIpXSwgYnk9InBoZV9jZW50cmUiKQpjb2xuYW1lcyhQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4pWzE1XSA8LSAiOCIKUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluIDwtIGxlZnRfam9pbihQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4sIFBIRS5nZW8uc3VibGluZWFnZVtQSEUuZ2VvLnN1YmxpbmVhZ2UkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2U9PSIxNCIsYygicGhlX2NlbnRyZSIsIkNvdW50IildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbilbMTZdIDwtICIxNCIKUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluIDwtIGxlZnRfam9pbihQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4sIFBIRS5nZW8uc3VibGluZWFnZVtQSEUuZ2VvLnN1YmxpbmVhZ2UkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2U9PSIxNSIsYygicGhlX2NlbnRyZSIsIkNvdW50IildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbilbMTddIDwtICIxNSIKUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluIDwtIGxlZnRfam9pbihQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4sIFBIRS5nZW8uc3VibGluZWFnZVtQSEUuZ2VvLnN1YmxpbmVhZ2UkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2U9PSIxNiIsYygicGhlX2NlbnRyZSIsIkNvdW50IildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbilbMThdIDwtICIxNiIKUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluIDwtIGxlZnRfam9pbihQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4sIFBIRS5nZW8uc3VibGluZWFnZVtQSEUuZ2VvLnN1YmxpbmVhZ2UkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2U9PSJTaW5nbGV0b24iLGMoInBoZV9jZW50cmUiLCJDb3VudCIpXSwgYnk9InBoZV9jZW50cmUiKQpjb2xuYW1lcyhQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4pWzE5XSA8LSAiU2luZ2xldG9uIgpQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW5baXMubmEoUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluKV0gPC0gMAoKIyBNb3N0IHNhbXBsZXMgYXJlIGVpdGhlciBzdWJsaW5lYWdlIDEgb3IgMTQuIExldCdzIGNyZWF0ZSBhIGNvdW50IG9mIHNhbXBsZXMgdGhhdCBhcmUgbmVpdGhlci4KUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluJGBPdGhlciBTdWJsaW5lYWdlc2AgPC0gc2FwcGx5KDE6bnJvdyhQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4pLCBmdW5jdGlvbiAoeCkgUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluJFJlZ2lvbl9Db3VudFt4XS1zdW0oUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluJGAxYFt4XSwgUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluJGAxNGBbeF0pKSAKCgoKVUsuZ2cuc2NhdHRlcnBpZS5zdWJsaW5lYWdlIDwtIFVLLmdnICsgZ2VvbV9zY2F0dGVycGllKGRhdGE9UEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluW1BIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiRtZWFuLmxvbmchPTAsXSwgYWVzKG1lYW4ubG9uZywgbWVhbi5sYXQsIGdyb3VwPXBoZV9jZW50cmUsIHI9cmFkaXVzLlVLKSwgYWxwaGE9MC44NSwgY29sb3I9TkEsIGNvbHM9YygiMSIsIjE0IiwiT3RoZXIgU3VibGluZWFnZXMiKSkgKyAKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJUUEFcblN1YmxpbmVhZ2UiLHZhbHVlcz1jKCIjRkM5MjcyIiwiI0JDQkREQyIsICJncmV5NTAiKSwgYnJlYWtzPWMoIjEiLCIxNCIsIk90aGVyIFN1YmxpbmVhZ2VzIikpCgojIGFkZCBsZWdlbmQKVUsuZ2cuc2NhdHRlcnBpZS5zdWJsaW5lYWdlIDwtIFVLLmdnLnNjYXR0ZXJwaWUuc3VibGluZWFnZSArIGdlb21fc2NhdHRlcnBpZV9sZWdlbmQoUEhFLnJlZ2lvbi5HUFMudWttYXBbIWlzLm5hKFBIRS5yZWdpb24uR1BTLnVrbWFwJG1lYW4ubGF0KSwicmFkaXVzLlVLIl0sIGxhYmVsbGVyPWZ1bmN0aW9uKHgpIHJvdW5kKCgxMF4oeC8yMDAwMCkpLDApLCBuPTMsIHg9MTUwMDAwLCB5PTUwMDAwMCkKCiNVSy5nZy5zY2F0dGVycGllIDwtIFVLLmdnLnNjYXR0ZXJwaWUgKyB4LnRoZW1lLnN0cmlwICsgeS50aGVtZS5zdHJpcApVSy5nZy5zY2F0dGVycGllLnN1YmxpbmVhZ2UgPC0gVUsuZ2cuc2NhdHRlcnBpZS5zdWJsaW5lYWdlICsgdGhlbWVfbm90aGluZygpCgojPyBBZGQgbGFiZWxzClVLLmdnLnNjYXR0ZXJwaWUuc3VibGluZWFnZSA8LSBVSy5nZy5zY2F0dGVycGllLnN1YmxpbmVhZ2UgKyBnZW9tX2xhYmVsX3JlcGVsKGRhdGE9UEhFLnJlZ2lvbi5HUFMudWttYXBbIWlzLm5hKFBIRS5yZWdpb24uR1BTLnVrbWFwJG1lYW4ubGF0KSxdLCBhZXMobWVhbi5sb25nLCBtZWFuLmxhdCwgbGFiZWw9cGhlX2NlbnRyZSksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgbnVkZ2VfeCA9IDUwMDAwLCBudWRnZV95ID0gLTI1MDAwLCBzZWdtZW50LnNpemUgID0gMC4xKSArCiAgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksIGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikgKyAKICB0aGVtZS50ZXh0LnNpemUgKwogIHRoZW1lX25vdGhpbmcoKQoKClVLLmdnLnNjYXR0ZXJwaWUuc3VibGluZWFnZQpgYGAKClwKQ29tYmluZWQgbWFwIHBsb3QKYGBge3J9ClVLLmdnLnNjYXR0ZXJwaWUuY29tYmkgPC0gcGxvdF9ncmlkKFVLLmdnLnNjYXR0ZXJwaWUubGFicywgVUsuZ2cuc2NhdHRlcnBpZS5zdWJsaW5lYWdlLCBuY29sPTIsIGxhYmVscyA9IGMoIkEiLCJCIiksIGxhYmVsX3NpemU9cGFuZWwubGFiLnNpemUpCgpVSy5nZy5zY2F0dGVycGllLmNvbWJpCmBgYApcClwKUGxvdCBpbiBjb21iaW5hdGlvbiB3aXRoIGJhcnBsb3RzCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04fQpwbG90X2dyaWQoVUsuZ2cuc2NhdHRlcnBpZS5jb21iaSwgUEhFLnJlZ2lvbi5jb21iaXBsb3QuMi5saW5lYWdlcywgbnJvdz0yLCByZWxfaGVpZ2h0cz1jKDQsNSkpCgojZ2dzYXZlKHBhc3RlMChGaWd1cmVfb3V0cHV0X2RpcmVjdG9yeSwiRmlnMl9UUEEtUEhFX01hcC1MaW5lYWdlK0JhcnBsb3RzLiIsZm9ybWF0KFN5cy5EYXRlKCksIiVZJW0lZCIpLCIucGRmIiksIHVuaXRzPSdtbScsIHdpZHRoPTE5MCwgaGVpZ2h0PTE4NSwgZGV2aWNlPSdwZGYnLCBkcGk9MTIwMCkKCiNnZ3NhdmUocGxvdD1wbG90X2dyaWQoVUsuZ2cuc2NhdHRlcnBpZS5jb21iaSwgUEhFLnJlZ2lvbi5jb21iaXBsb3QuMi5saW5lYWdlcywgbnJvdz0yLCByZWxfaGVpZ2h0cz1jKDQsNSkpLCBwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksIkZpZzJfVFBBLVBIRV9NYXAtTGluZWFnZStCYXJwbG90cy4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnN2ZyIpLCB1bml0cz0nbW0nLCB3aWR0aD0xOTAsIGhlaWdodD0xODUsIGRldmljZT1zdmdsaXRlLCBkcGk9MTIwMCkKCgpgYGAKXApcCiMjIyBBbmFseXNpcyBieSBzdWJsaW5lYWdlClwKTm93IGxldHMgc3RhcnQgZXhwbG9yaW5nIGhvdyBzYW1wbGVzIGFyZSBkaXN0cmlidXRlZCBieSBzdWJsaW5lYWdlCgpgYGB7cn0KUEhFLm1ldGFkYXRhLmxpbmtlZCA8LSBQSEUubWV0YWRhdGEubGlua2VkClBIRS5tZXRhZGF0YS5saW5rZWQkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UgPC0gZmFjdG9yKFBIRS5tZXRhZGF0YS5saW5rZWQkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGxldmVscz1yZXYoYXMuY2hhcmFjdGVyKHNvcnQodW5pcXVlKFBIRS5tZXRhZGF0YS5saW5rZWQkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpKSkpKQoKUEhFLkxpbmVhZ2UuY291bnQgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KFRQQV9MaW5lYWdlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbD1zdW0oQ291bnQpLCBwZXJjPShDb3VudC90b3RhbCkqMTAwKQoKUEhFLnN1Ymxpbi5jb3VudCA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsPXN1bShDb3VudCksIHBlcmM9KENvdW50L3RvdGFsKSoxMDApCgpQSEUuZ2VvLnN1Ymxpbi55ZWFycyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UseWVhcikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpCgoKIyMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBzdWJsaW5lYWdlIGdyb3VwcwoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IGdlbmRlciBvcmllbnRhdGlvbgpQSEUuc3VibGluZWFnZS5vcmllbnRhdGlvbi5jb3VudHMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLGdlbmRlcl9vcmllbnRhdGlvbikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuc3VibGluPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoZ2VuZGVyX29yaWVudGF0aW9uKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwuc3VibGluLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpCgoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IFVLIGJvcm4KUEhFLnN1YmxpbmVhZ2UuVUtib3JuIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgdWtib3JuKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5zdWJsaW49c3VtKENvdW50KSkgJT4lCiAgI2RwbHlyOjphcnJhbmdlKGRlc2ModWtib3JuKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2ModWtib3JuKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwuc3VibGluLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpCiAgCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBMb25kb24gYmFzZWQKUEhFLnN1YmxpbmVhZ2UuTG9uZG9uIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgbG9uZG9uKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5zdWJsaW49c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhsb25kb24pLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5zdWJsaW4sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBBZ2UgZ3JvdXAKUEhFLnN1YmxpbmVhZ2UuQWdlIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgYWdlX2dyb3VwKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5zdWJsaW49c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhhZ2VfZ3JvdXApLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5zdWJsaW4sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBISVYgZ3JvdXAKUEhFLnN1YmxpbmVhZ2UuSElWIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgaGl2cG9zKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5zdWJsaW49c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhoaXZwb3MpLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5zdWJsaW4sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBieSBQSEUgUmVnaW9uClBIRS5zdWJsaW5lYWdlLlBIRWNlbnRyZSA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIHBoZV9jZW50cmUpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnN1Ymxpbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKHBoZV9jZW50cmUpLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5zdWJsaW4sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCmBgYAoKXApQbG90IGJ5IHN1YmxpbmVhZ2UKYGBge3J9CnAuc3VibGluZWFnZS55ZWFyLmJ1YmJsZXBsb3QgPC0gZ2dwbG90KFBIRS5nZW8uc3VibGluLnllYXJzLCBhZXMoYXMubnVtZXJpYyh5ZWFyKSwgVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGNvbG91cj1UUEEucGluZWNvbmUuc3VibGluZWFnZSkpICsKICBnZW9tX3BvaW50KGFscGhhPTAuNjUsIGFlcyhzaXplPUNvdW50KSkgKyAKICBnZW9tX2xpbmUoYWxwaGE9MC4yNSkgKwogIGd1aWRlcyhjb2xvdXI9J25vbmUnKSArCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gNyxicmVha3M9YygxLDUsMTAsMjUsNTApKSArIAogIGd1aWRlcyhzaXplPWd1aWRlX2xlZ2VuZChucm93PTIsIGRpcmVjdGlvbiA9ICdob3Jpem9udGFsJywgYnlyb3c9VCkpICsKICB0aGVtZV9saWdodCgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iVFBBXG5TdWJsaW5lYWdlIix2YWx1ZXM9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UuY29scywgYnJlYWtzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgbGFicyh5PSJUUEEgU3VibGluZWFnZSIsIHg9IlNhbXBsZSBZZWFyIiwgc2l6ZT0iQ291bnQiKSAKI3Auc3VibGluZWFnZS55ZWFyLmJ1YmJsZXBsb3QKCnAuc3VibGluZWFnZS5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLnN1Ymxpbi5jb3VudCwgYWVzKENvdW50LFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLGZpbGw9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249InN0YWNrIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlRQQVxuU3VibGluZWFnZSIsdmFsdWVzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLmNvbHMsIGJyZWFrcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSkgKwogIGxhYnMoeT0iVFBBIFN1YmxpbmVhZ2UiLCB4PSJTYW1wbGUgQ291bnQiKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLnN1Ymxpbi5jb3VudCwgYWVzKChDb3VudCsxMiksIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpICsKICAjY29vcmRfY2FydGVzaWFuKHhsaW09YygwLDIwMCkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKDAsMjYwKSkgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChuY29sPTIpKQojcC5zdWJsaW5lYWdlLmhiYXJwbG90IAoKcC5zdWJsaW5lYWdlLm9yaWVudGF0aW9uLmhiYXJwbG90IDwtIGdncGxvdChQSEUuc3VibGluZWFnZS5vcmllbnRhdGlvbi5jb3VudHMsIGFlcyh5PVRQQS5waW5lY29uZS5zdWJsaW5lYWdlLHg9Q291bnQsZmlsbD1nZW5kZXJfb3JpZW50YXRpb24pKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iT3JpZW50YXRpb24iLHZhbHVlcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbi5jb2xzLCBicmVha3M9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24pICsKICBsYWJzKHk9IlRQQSBTdWJsaW5lYWdlIiwgeD0iT3JpZW50YXRpb24iKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5jb2w9MSkpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuc3VibGluZWFnZS5vcmllbnRhdGlvbi5jb3VudHMsIGFlcyhjdW1fZnJhY3QubWlkLCBUUEEucGluZWNvbmUuc3VibGluZWFnZSxsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQojcC5yZWdpb24ub3JpZW50YXRpb24uaGJhcnBsb3QKCnAuc3VibGluZWFnZS5oaXYuaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5zdWJsaW5lYWdlLkhJViwgYWVzKHk9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIHg9Q291bnQsZmlsbD1oaXZwb3MpKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iSElWICt2ZSIsdmFsdWVzPVBIRS5oaXYuY29scyRoaXYuY29scywgYnJlYWtzPVBIRS5oaXYuY29scyRoaXZwb3MpICsKICBsYWJzKHk9IlRQQSBTdWJsaW5lYWdlIiwgeD0iSElWICt2ZSIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobmNvbD0xKSkgKyAKICBnZW9tX3RleHQoZGF0YT1QSEUuc3VibGluZWFnZS5ISVYsIGFlcyhjdW1fZnJhY3QubWlkLCBUUEEucGluZWNvbmUuc3VibGluZWFnZSxsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQojcC5zdWJsaW5lYWdlLmhpdi5oYmFycGxvdAoKcC5zdWJsaW5lYWdlLnVrYm9ybi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLnN1YmxpbmVhZ2UuVUtib3JuLCBhZXMoeT1UUEEucGluZWNvbmUuc3VibGluZWFnZSx4PUNvdW50LGZpbGw9dWtib3JuKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlVLXG5ib3JuIix2YWx1ZXM9UEhFLnVrYm9ybi5jb2xzJHVrYm9ybi5jb2xzLCBicmVha3M9UEhFLnVrYm9ybi5jb2xzJHVrYm9ybikgKwogIGxhYnMoeT0iVFBBIFN1YmxpbmVhZ2UiLCB4PSJVSyBib3JuIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChucm93PTMpKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLnN1YmxpbmVhZ2UuVUtib3JuLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikKI3Auc3VibGluZWFnZS51a2Jvcm4uaGJhcnBsb3QKCnAuc3VibGluZWFnZS5BZ2UuaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5zdWJsaW5lYWdlLkFnZSwgYWVzKHk9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIHg9Q291bnQgLGZpbGw9YWdlX2dyb3VwKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkFnZVxuR3JvdXAiLHZhbHVlcz1QSEUuQWdlLmNvbHMkYWdlX2dyb3VwLmNvbHMsIGJyZWFrcz1QSEUuQWdlLmNvbHMkYWdlX2dyb3VwKSArCiAgbGFicyh5PSJUUEEgU3VibGluZWFnZSIsIHg9IkFnZSBHcm91cCIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobmNvbD0xKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5zdWJsaW5lYWdlLkFnZSwgYWVzKGN1bV9mcmFjdC5taWQsIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpCiNwLnN1YmxpbmVhZ2UuQWdlLmhiYXJwbG90CgoKcC5zdWJsaW5lYWdlLlBIRXJlZ2lvbi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLnN1YmxpbmVhZ2UuUEhFY2VudHJlLCBhZXMoeT1UUEEucGluZWNvbmUuc3VibGluZWFnZSwgeD1Db3VudCwgZmlsbD1waGVfY2VudHJlKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlVLSFNBXG5SZWdpb24iLHZhbHVlcz1QSEUucmVnaW9uLmNvbHMuYnJldyRyZWdpb24uY29sLCBicmVha3M9UEhFLnJlZ2lvbi5jb2xzLmJyZXckUEhFLnJlZ2lvbikgKwogIGxhYnMoeT0iVFBBIFN1YmxpbmVhZ2UiLCB4PSJVS0hTQSBSZWdpb24iKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9NCkpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuc3VibGluZWFnZS5QSEVjZW50cmUsIGFlcyhjdW1fZnJhY3QubWlkLCBUUEEucGluZWNvbmUuc3VibGluZWFnZSxsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQoKYGBgClwKTG9vayBhdCBob3cgc3VibGluZWFnZXMgYXJlIGRpc3RyaWJ1dGVkIGJ5IHJlZ2lvbiAoc3VibGluZWFnZS1jZW50cmljKQpgYGB7cn0KcC5zdWJsaW5lYWdlLlBIRXJlZ2lvbi5oYmFycGxvdApgYGAKClwKQ29tYmluZSBwYXRpZW50IG1ldGFkYXRhIGludG8gYSBwbG90CmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTB9CiNQSEUuc3VibGluZWFnZXMuY29tYmlwbG90LjEgPC0gcGxvdF9ncmlkKHAuc3VibGluZWFnZS55ZWFyLmJ1YmJsZXBsb3QsIHAuc3VibGluZWFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5vcmllbnRhdGlvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5oaXYuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2UuUEhFcmVnaW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLnVrYm9ybi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5BZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBucm93PTEsIGFsaWduPSJoIiwgcmVsX3dpZHRocz1jKDMsMiwyLDIsMiwyLDIpLCBzY2FsZT0wLjkpCgojUEhFLnN1YmxpbmVhZ2VzLmNvbWJpcGxvdC4xIDwtIHBsb3RfZ3JpZChwLnN1YmxpbmVhZ2UueWVhci5idWJibGVwbG90LCBwLnN1YmxpbmVhZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2Uub3JpZW50YXRpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2UuaGl2LmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLkFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5QSEVyZWdpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBucm93PTEsIGFsaWduPSJoIiwgcmVsX3dpZHRocz1jKDMsMiwyLDIsMiw0KSwgc2NhbGU9MC45KQoKUEhFLnN1YmxpbmVhZ2VzLmNvbWJpcGxvdC4xIDwtIHBsb3RfZ3JpZChwLnN1YmxpbmVhZ2UueWVhci5idWJibGVwbG90LCBwLnN1YmxpbmVhZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2Uub3JpZW50YXRpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2UuaGl2LmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLkFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIG5yb3c9MSwgYWxpZ249ImgiLCByZWxfd2lkdGhzPWMoNCwyLDIsMiwyKSwgc2NhbGU9MC45KQoKUEhFLnN1YmxpbmVhZ2VzLmNvbWJpcGxvdC4xIAoKYGBgCgoKXApMZXRzIGFkZCB0aGUgJ2FsbCcgcm93IGFnYWluIHRvIHRoZSAnYnkgc3VibGluZWFnZScgcGxvdApgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTJ9CiMgbGVnZW5kcwpQSEUuc3VibGluZWFnZS5jb21iaXBsb3QuMS5sZWdlbmRzIDwtIHBsb3RfZ3JpZChnZXRfbGVnZW5kKHAuc3VibGluZWFnZS55ZWFyLmJ1YmJsZXBsb3QpLCBnZXRfbGVnZW5kKHAuc3VibGluZWFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXApLCBnZXRfbGVnZW5kKHAuc3VibGluZWFnZS5vcmllbnRhdGlvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXApLCBnZXRfbGVnZW5kKHAuc3VibGluZWFnZS5oaXYuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwKSwgZ2V0X2xlZ2VuZChwLnN1YmxpbmVhZ2UuQWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCksIG5yb3c9MSwgYWxpZ249ImgiLCByZWxfd2lkdGhzPWMoNiw0LDQsNCw0KSwgc2NhbGU9MC45NSkKCiMgcmVnaW9ucwojUEhFLnN1YmxpbmVhZ2UuY29tYmlwbG90LjEubm9sZWdlbmQgPC0gcGxvdF9ncmlkKHAuc3VibGluZWFnZS55ZWFyLmJ1YmJsZXBsb3QgKyBsZWdlbmQuc3RyaXAsIHAuc3VibGluZWFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIHAuc3VibGluZWFnZS5vcmllbnRhdGlvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIHAuc3VibGluZWFnZS5oaXYuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICsgbGVnZW5kLnN0cmlwLCBwLnN1YmxpbmVhZ2UuQWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCArIGxlZ2VuZC5zdHJpcCwgbnJvdz0xLCBhbGlnbj0iaCIsIHJlbF93aWR0aHM9Yyg0LDIsMiwyLDIpLCBzY2FsZT0wLjkpCgojIE9yIGRvIGl0IHZlcnRpY2FsbHkKcC5zdWJsaW5lYWdlLnllYXIuYnViYmxlcGxvdC5jb21iaSA8LSBwbG90X2dyaWQocC5hbGwueWVhci5idWJibGVwbG90ICsgeC50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLnllYXIuYnViYmxlcGxvdCArIGxlZ2VuZC5zdHJpcCwgbmNvbD0xLCBhbGlnbj0idiIsYXhpcz0ibHIiLCByZWxfaGVpZ2h0cz1jKDEsNykpCgpwLnN1YmxpbmVhZ2UuaGJhci5jb3VudHMuY29tYmkgPC0gcGxvdF9ncmlkKHAuYWxsLmhiYXJwbG90ICsgeC50aGVtZS5zdHJpcCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIG5jb2w9MSwgYWxpZ249InYiLGF4aXM9ImxyIiwgcmVsX2hlaWdodHM9YygxLDcpKQoKcC5zdWJsaW5lYWdlLmhiYXIub3JpZW50YXRpb24uY29tYmkgPC0gcGxvdF9ncmlkKHAuYWxsLm9yaWVudGF0aW9uLmhiYXJwbG90ICsgeC50aGVtZS5zdHJpcCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5vcmllbnRhdGlvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIG5jb2w9MSwgYWxpZ249InYiLGF4aXM9ImxyIiwgcmVsX2hlaWdodHM9YygxLDcpKQoKcC5zdWJsaW5lYWdlLmhiYXIuaGl2LmNvbWJpIDwtIHBsb3RfZ3JpZChwLmFsbC5oaXYuaGJhcnBsb3QgKyB4LnRoZW1lLnN0cmlwICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLmhpdi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIG5jb2w9MSwgYWxpZ249InYiLGF4aXM9ImxyIiwgcmVsX2hlaWdodHM9YygxLDcpKQoKcC5zdWJsaW5lYWdlLmhiYXIuQWdlLmNvbWJpIDwtIHBsb3RfZ3JpZChwLmFsbC5BZ2UuaGJhcnBsb3QgKyB4LnRoZW1lLnN0cmlwICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLkFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIG5jb2w9MSwgYWxpZ249InYiLGF4aXM9ImxyIiwgcmVsX2hlaWdodHM9YygxLDcpKQoKIyBDb21iaW5lIHRoZSBwbG90cwpwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbCA8LSBwbG90X2dyaWQocC5zdWJsaW5lYWdlLnllYXIuYnViYmxlcGxvdC5jb21iaSwgcC5zdWJsaW5lYWdlLmhiYXIuY291bnRzLmNvbWJpLCBwLnN1YmxpbmVhZ2UuaGJhci5vcmllbnRhdGlvbi5jb21iaSwgcC5zdWJsaW5lYWdlLmhiYXIuaGl2LmNvbWJpLCBwLnN1YmxpbmVhZ2UuaGJhci5BZ2UuY29tYmksIG5yb3c9MSwgcmVsX3dpZHRocz1jKDcsMyw0LDQsNCksIGxhYmVscz1jKCJBIiwgIkIiLCAiQyIsICJEIiwgIkUiKSxsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplLCB2anVzdD0xLCBzY2FsZT0wLjk5KQoKIyBhbmQgYWRkIHRoZSBsZWdlbmRzIG9uIHRvcAojcC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwud2l0aC5sZWdlbmRzIDwtIHBsb3RfZ3JpZChQSEUuc3VibGluZWFnZS5jb21iaXBsb3QuMS5sZWdlbmRzLCBwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbCwgbmNvbD0xLCByZWxfaGVpZ2h0cz1jKDEsOSkpCgojIGxlZ2VuZHMgYmVsb3cKcC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwud2l0aC5sZWdlbmRzIDwtIHBsb3RfZ3JpZChwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbCwgUEhFLnN1YmxpbmVhZ2UuY29tYmlwbG90LjEubGVnZW5kcywgbmNvbD0xLCByZWxfaGVpZ2h0cz1jKDgsMSkpCgoKcC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwud2l0aC5sZWdlbmRzCgpgYGAKClwKXCAKVGhlc2UgcGF0dGVybnMgbG9vayBmYWlybHkgc2ltaWxhciBiZXR3ZWVuIHN1YmxpbmVhZ2VzLCBhbmQgKGFwYXJ0IGZyb20gMSAmIDE0KSB0aGUgZ3JvdXBzIGFyZSB2ZXJ5IHNtYWxsLiBIb3dldmVyLCBzdWJsaW5lYWdlIDE0IGRvZXMgYXBwZWFyIHRvIGhhdmUgYSBoaWdoZXIgcHJvcG9ydGlvbiBvZiBNU00gY29tcGFyZWQgdG8gc3VibGluZWFnZSAxIGFuZCBvdGhlcnMuIExldCdzIHRlc3QgdGhhdCBmb3JtYWxseSB1c2luZyAyeDIgZmlzaGVyJ3MgdGVzdHMKXApgYGB7cn0KUEhFLk1TTS5jb3VudHMuYWxsIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShpcy5NU00sIC5kcm9wPUYpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnN1Ymxpbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZSgoaXMuTVNNKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwuc3VibGluLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpCgpQSEUuc3VibGluZWFnZS5NU00uY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSxpcy5NU00sIC5kcm9wPUYpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnN1Ymxpbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZSgoaXMuTVNNKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwuc3VibGluLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpICMlPiUKICAjZHBseXI6OmZpbHRlcighaXMubmEoaXMuTVNNKSkKCgpQSEUuc3VibGluZWFnZS5NU00uY291bnRzLndpZGVyIDwtIFBIRS5zdWJsaW5lYWdlLk1TTS5jb3VudHMgJT4lIGRwbHlyOjpzZWxlY3QoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGlzLk1TTSwgQ291bnQpICU+JQogIHRpZHlyOjpwaXZvdF93aWRlcihuYW1lc19mcm9tID0gaXMuTVNNLCB2YWx1ZXNfZnJvbT1Db3VudCkgJT4lCiAgZHBseXI6Om11dGF0ZShNU009cmVwbGFjZV9uYShNU00sIDApLCBPdGhlcj1yZXBsYWNlX25hKE90aGVyLCAwKSwgVG90YWw9c3VtKE1TTSxPdGhlcikpICU+JQogICNkcGx5cjo6c2VsZWN0KC1gTkFgKSAlPiUKICBkcGx5cjo6ZmlsdGVyKFRvdGFsIT0wKQogIAoKUEhFLnN1YmxpbmVhZ2UuTVNNLnB2YWwgPC0gZGF0YS5mcmFtZShUUEEucGluZWNvbmUuc3VibGluZWFnZT1QSEUuc3VibGluZWFnZS5NU00uY291bnRzLndpZGVyJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBwLmZpc2hlcj1zYXBwbHkoMTpucm93KFBIRS5zdWJsaW5lYWdlLk1TTS5jb3VudHMud2lkZXIpLCBmdW5jdGlvbiAoeCkgZmlzaGVyLnRlc3QobWF0cml4KGFzLm51bWVyaWMoYyhQSEUuc3VibGluZWFnZS5NU00uY291bnRzLndpZGVyW3gsIk1TTSJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBIRS5zdWJsaW5lYWdlLk1TTS5jb3VudHMud2lkZXJbeCwiT3RoZXIiXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQSEUuTVNNLmNvdW50cy5hbGxbUEhFLk1TTS5jb3VudHMuYWxsJGlzLk1TTT09Ik1TTSIsIkNvdW50Il0sIFBIRS5NU00uY291bnRzLmFsbFtQSEUuTVNNLmNvdW50cy5hbGwkaXMuTVNNPT0iT3RoZXIiLCJDb3VudCJdKSksbnJvdz0yKSlbWzFdXSksIHN0cmluZ3NBc0ZhY3RvcnM9RikKClBIRS5zdWJsaW5lYWdlLk1TTS5jb3VudHMud2lkZXIgPC0gZHBseXI6OmxlZnRfam9pbihQSEUuc3VibGluZWFnZS5NU00uY291bnRzLndpZGVyLCBQSEUuc3VibGluZWFnZS5NU00ucHZhbCwgYnk9IlRQQS5waW5lY29uZS5zdWJsaW5lYWdlIikKClBIRS5zdWJsaW5lYWdlLk1TTS5jb3VudHMud2lkZXIKYGBgCgoKXApcCiMjIyBWaXN1YWxpc2F0aW9uIG9mIFVLIGdlbm9taWMgcmVsYXRpb25zaGlwcwpcCk9rLCBsZXQncyBtYWtlIGEgdHJlZSBmb3IgZGlzcGxheWluZyB0aGVzZSByZWxhdGlvbnNoaXBzIHVzaW5nIHRoZSBVSyBkYXRhc2V0IG9ubHkKXApGcm9tIHNvbWUgZXhwZXJpbWVudGF0aW9uLCBhICdHcmFwZVRyZWUnIG1pbmltdW0gc3Bhbm5pbmcgbmV0d29yayB3b3JrcyB3ZWxsIGZvciB2aXN1YWxpc2luZyB0aGUgY2xvbmFsaXR5IG9mIHRoZXNlIHBvcHVsYXRpb25zLiBXZSBjYW4gdXNlIGEgU05QLXNjYWxlZCBwaHlsb2dlbnkgYXMgZGlyZWN0IGlucHV0IHRvIEdyYXBlVHJlZSwgYW5kIHRoaXMgd2lsbCBhbGxvdyBicmFuY2hlcyB0byBiZSBzY2FsZWQgYXBwcm9wcmlhdGVseS4gSG93ZXZlciwgYWx0aG91Z2ggYW5ub3RhdGlvbiBpcyBhbGxvd2VkIHdpdGhpbiB0aGUgR3JhcGVUcmVlIHNvZnR3YXJlLCBjb2xvdXJzIG11c3QgYmUgbWFudWFsbHkgZWRpdGVkLiBGaW5hbCBHcmFwZVRyZWUgcGxvdHMgY2FuIHRoZW4gYmUgaW1wb3J0ZWQgYmFjayBpbnRvIFIgZm9yIGNvbWJpbmluZyB3aXRoIG90aGVyIHBsb3RzLiAKXAoKQWx0ZXJuYXRpdmUgdmlzdWFsaXNhdGlvbnMgLSBncmFwZXRyZWU/ClwKVGFrZSB0aGUgNTI2LWdsb2JhbCBwaHlsb2dlbnkgKHNucC1zY2FsZWQgdmVyc2lvbiBmcm9tIHB5amFyKSwgYW5kIHBydW5lIHRvIG9ubHkgaW5jbHVkZSB0aGUgVUsgc3RyYWlucyBmcm9tIHRoaXMgc3R1ZHkgLSB0aGlzIGVuc3VyZXMgdGhlIHRvcG9sb2d5IGlzIGNvbnNpc3RlbnQgYWNjcm9zcyBzdHVkaWVzLiAKYGBge3J9CgpUUEEucHlqYXIudHJlZS5zdWJzZXQudWsgPC0gYXBlOjprZWVwLnRpcChUUEEucHlqYXIudHJlZSwgYXMuY2hhcmFjdGVyKHVubGlzdChQSEUubWV0YWRhdGEubGlua2VkW1BIRS5tZXRhZGF0YS5saW5rZWQkR2VvX0NvdW50cnk9PSJVSyIsIlNhbXBsZV9OYW1lIl0pKSkKCgpUUEEucHlqYXIudHJlZS5zdWJzZXQuZ2xvYmFsX2JlYXN0X29ubHkuc2VxbGFuZXMgPC0gVFBBLm1ldGEyLjEgJT4lIGZpbHRlcihmdWxsLnRlbXBvcmFsLmFuYWx5c2lzPT0nWWVzJykgJT4lCiAgc2VsZWN0KENsZWFuZWRfZmFzdHFfaWQpICU+JSBwdWxsKCkKClRQQS5weWphci50cmVlLnN1YnNldC51ay5zZXFsYW5lcyA8LSBhcy5jaGFyYWN0ZXIodW5saXN0KFBIRS5tZXRhZGF0YS5saW5rZWRbUEhFLm1ldGFkYXRhLmxpbmtlZCRHZW9fQ291bnRyeT09IlVLIiwiQ2xlYW5lZF9mYXN0cV9pZCJdKSkKCgpnZ3RyZWUoVFBBLnB5amFyLnRyZWUuc3Vic2V0LnVrKQojd3JpdGUudHJlZShUUEEucHlqYXIudHJlZS5zdWJzZXQudWssIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiVFBBLlVLLW9ubHkucHlqYXIuMjAyMi0wMi0wMy50cmUiKSkKCiMgV3JpdGUgb3V0IGEgbWV0YWRhdGEgc2hlZXQgZm9yIHRoZSByZWxldmFudCBpbmZvcm1hdGlvbgpQSEUubWV0YWRhdGEubGlua2VkLmdyYXBldHJlZSA8LSBQSEUubWV0YWRhdGEubGlua2VkWyxjKCJTYW1wbGVfTmFtZSIsICJ5ZWFyIiwiZ2VuZGVyX29yaWVudGF0aW9uIiwicGhlX2NlbnRyZSIsImhpdnBvcyIsInVrYm9ybiIsIlRQQV9MaW5lYWdlIiwiVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UiKV0KY29sbmFtZXMoUEhFLm1ldGFkYXRhLmxpbmtlZC5ncmFwZXRyZWUpWzFdIDwtICJJRCIKCiN3cml0ZS50YWJsZShQSEUubWV0YWRhdGEubGlua2VkLmdyYXBldHJlZSwgcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEuVUstb25seS5ncmFwZXRyZWUubWV0YS4yMDIyLTAyLTAzLnRzdiIpLCBzZXAgPSAiXHQiLCBxdW90ZT1GLCByb3cubmFtZXMgPSBGKQpgYGAKXApUcmVlIGluZGVwZW5kZW50bHkgdmlzdWFsaXNlZCBhbmQgYW5ub3RhdGVkIHVzaW5nIEdyYXBlVHJlZS4KXApOb3cgaW1wb3J0IGFuZCBpbnRlZ3JhdGUgR3JhcGVUcmVlIHBsb3Qgd2l0aCBtZXRhZGF0YSBwbG90cy4KYGBge3IsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTEwfQojIENvbWJpbmUgdGhlIHBsb3RzCnAuc3VibGluZWFnZS5oYmFyLmNvbWJpLnBsdXMuYWxsLkIyRiA8LSBwbG90X2dyaWQocC5zdWJsaW5lYWdlLnllYXIuYnViYmxlcGxvdC5jb21iaSwgcC5zdWJsaW5lYWdlLmhiYXIuY291bnRzLmNvbWJpLCBwLnN1YmxpbmVhZ2UuaGJhci5vcmllbnRhdGlvbi5jb21iaSwgcC5zdWJsaW5lYWdlLmhiYXIuaGl2LmNvbWJpLCBwLnN1YmxpbmVhZ2UuaGJhci5BZ2UuY29tYmksIG5yb3c9MSwgcmVsX3dpZHRocz1jKDcsNCw0LDQsNCksIGxhYmVscz1jKCJCIiwgIkMiLCAiRCIsICJFIiwgIkYiKSxsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplLCB2anVzdD0xLCBzY2FsZT0wLjk3KQoKIyBsZWdlbmRzIGJlbG93CnAuc3VibGluZWFnZS5oYmFyLmNvbWJpLnBsdXMuYWxsLndpdGgubGVnZW5kcy5CMkYgPC0gcGxvdF9ncmlkKHAuc3VibGluZWFnZS5oYmFyLmNvbWJpLnBsdXMuYWxsLkIyRiwgUEhFLnN1YmxpbmVhZ2UuY29tYmlwbG90LjEubGVnZW5kcywgbmNvbD0xLCByZWxfaGVpZ2h0cz1jKDcsMSkpCgojcC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwud2l0aC5sZWdlbmRzLkIyRgoKCiMgTm93IGJyaW5nIGluIGV4dGVybmFsbHkgcGxvdHRlZCBHcmFwZXRyZWUKcC5UUEEuVUsuR3JhcGV0cmVlLnN1YmxpbmVhZ2VzIDwtIGdnZHJhdygpICsgZHJhd19pbWFnZShUUEEuVUsuR3JhcGV0cmVlLnN1YmxpbmVhZ2VzLmZpbGUpCnAuVFBBLlVLLkdyYXBldHJlZS5zdWJsaW5lYWdlcwoKcC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwud2l0aC5sZWdlbmRzLkIyRi53aXRoLmdyYXBldHJlZSA8LSBwbG90X2dyaWQocC5UUEEuVUsuR3JhcGV0cmVlLnN1YmxpbmVhZ2VzLCBwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbC53aXRoLmxlZ2VuZHMuQjJGLCBuY29sPTEsIGxhYmVscz1jKCJBIiwiIiksIGxhYmVsX3NpemU9cGFuZWwubGFiLnNpemUsIHJlbF9oZWlnaHRzPWMoMyw1KSkgCgoKcC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwud2l0aC5sZWdlbmRzLkIyRi53aXRoLmdyYXBldHJlZQojZ2dzYXZlKHBhc3RlMChGaWd1cmVfb3V0cHV0X2RpcmVjdG9yeSwgIkZpZzFfVFBBLVBIRV9TYW1wbGUtZGlzdHJvcy1zdWJsaW5lYWdlLiIsZm9ybWF0KFN5cy5EYXRlKCksIiVZJW0lZCIpLCIucGRmIiksIHVuaXRzPSdtbScsIHdpZHRoPTE5MCwgaGVpZ2h0PTE4NSwgZGV2aWNlPSdwZGYnLCBkcGk9MTIwMCkKCiNnZ3NhdmUocGxvdD1wLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbC53aXRoLmxlZ2VuZHMuQjJGLndpdGguZ3JhcGV0cmVlLCBwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksICJGaWcxX1RQQS1QSEVfU2FtcGxlLWRpc3Ryb3Mtc3VibGluZWFnZS4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnN2ZyIpLCB1bml0cz0nbW0nLCB3aWR0aD0xOTAsIGhlaWdodD0xODUsIGRldmljZT1zdmdsaXRlLCBkcGk9MTIwMCkKCmBgYAoKXApNYW5hZ2Ugb3RoZXIgR3JhcGVUcmVlIHBsb3RzIChmb3IgY29uc2lzdGVuY3kpCgpUUEEtVUstMjAyMi0wMi0xNi4tTVNUcmVlXzMtd2F5LWZpZ3VyZS5JbnNjYXBlZC0yCmBgYHtyfQojIEJyaW5nIGluIDMtd2F5IGdyYXBoZXRyZWUgcGxvdCAoMyBkaWZmZXJlbnQgbWV0YWRhdGEgdmFyaWFibGVzIHVzaW5nIHRoZSBzYW1lIGlucHV0IHRyZWUpClRQQS5VSy5HcmFwZXRyZWUuM3dheSA8LSBnZ2RyYXcoKSArIGRyYXdfaW1hZ2UoVFBBLlVLLkdyYXBldHJlZS4zd2F5LmZpbGUpClRQQS5VSy5HcmFwZXRyZWUuM3dheQoKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksICJTdXBGaWc0X1RQQS1QSEVfR3JhcGV0cmVlLTN3YXlzLiIsZm9ybWF0KFN5cy5EYXRlKCksIiVZJW0lZCIpLCIucGRmIiksIHVuaXRzPSdtbScsIHdpZHRoPTE0NSwgaGVpZ2h0PTE4MCwgZGV2aWNlPSdwZGYnLCBkcGk9MTIwMCkKCmBgYAoKXApBbmQgYWxzbyBkbyB0aGUgSElWIHN0YXR1cyBwbG90CmBgYHtyfQoKVFBBLlVLLkdyYXBldHJlZS5ISVYgPC0gZ2dkcmF3KCkgKyBkcmF3X2ltYWdlKFRQQS5VSy5HcmFwZXRyZWUuSElWLmZpbGUpClRQQS5VSy5HcmFwZXRyZWUuSElWCgojZ2dzYXZlKHBhc3RlMChGaWd1cmVfb3V0cHV0X2RpcmVjdG9yeSwgIlN1cEZpZzVfVFBBLVBIRV9HcmFwZXRyZWUtSElWLiIsZm9ybWF0KFN5cy5EYXRlKCksIiVZJW0lZCIpLCIucGRmIiksIHVuaXRzPSdtbScsIHdpZHRoPTE4NSwgaGVpZ2h0PTExMCwgZGV2aWNlPSdwZGYnLCBkcGk9MTIwMCkKCmBgYAoKCgpcClwKIyMjIFBoeWxvZ2VuZXRpYyBjb250ZXh0IGFuYWx5c2VzClwKT2ssIG5vdyBsZXRzIGxvb2sgYXQgc29tZSB0cmVlcwpcCkZpcnN0LCBsZXQncyBmb3JtYWxpc2UgQkVBU1QgdHJlZSBwbG90dGluZyBhcyB0aHJlZSBzZXBhcmF0ZSBmdW5jdGlvbnMgdG8gZW5hYmxlIG90aGVyIHRyZWVzIHRvIGJlIHBsb3R0ZWQgdGhlIHNhbWUgd2F5ClwKYGBge3J9CmZ1bGwuYmVhc3QyLnRyZWUgPC0gcmVhZC5iZWFzdChmdWxsLmJlYXN0Mi50cmVlLmZpbGUpCmZ1bGwuYmVhc3QyLnRyZWVAcGh5bG8kdGlwLmxhYmVsIDwtIGdzdWIoIlxcfC4rJCIsIiIsZnVsbC5iZWFzdDIudHJlZUBwaHlsbyR0aXAubGFiZWwsIHBlcmw9VCkKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIGZ1bmN0aW9uIHRvIGV4dHJhY3QgYSB0cmVlIGJhc2VkIG9uIHN1YmxpbmVhZ2UKRXh0cmFjdF9zdWJsaW5lYWdlX3RyZWVfZm9yX3Bsb3QgPC0gZnVuY3Rpb24obXkuYmVhc3QudHJlZSwgbXkubWV0YWRhdGEsIG15LnBoZS5tZXRhLCBteS5zdWJsaW5lYWdlKXsKICAjIGdldCBhbGwgdGlwcyB0byBpbmNsdWRlIGZyb20gbWV0YWRhdGEsIHRoZW4gY2FsY3VsYXRlIE1SQ0EgZnJvbSB0cmVlCiAgc3VibGluZWFnZS50ZXN0Lm1yY2EgPC0gZ2V0TVJDQShteS5iZWFzdC50cmVlQHBoeWxvLCBhcy5jaGFyYWN0ZXIodW5saXN0KG15Lm1ldGFkYXRhW215Lm1ldGFkYXRhJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlPT1teS5zdWJsaW5lYWdlLCJTYW1wbGVfTmFtZSJdKSkpCiAgIyMjIyMjCiAgVFBBLmJlYXN0LnN1YnRyZWUudGVzdCA8LSB0cmVlX3N1YnNldChteS5iZWFzdC50cmVlLCBub2RlPXN1YmxpbmVhZ2UudGVzdC5tcmNhLCBsZXZlbHNfYmFjaz0wKQogIHJldHVybihUUEEuYmVhc3Quc3VidHJlZS50ZXN0KQp9CiNFeHRyYWN0X3N1YmxpbmVhZ2VfdHJlZV9mb3JfcGxvdChmdWxsLmJlYXN0Mi50cmVlLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgbXkuc3VibGluZWFnZSA9IDEpCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIEZ1bmN0aW9uIHRvIHByZXBhcmUgYSBiZWFzdCB0cmVlIHdpdGggdGltZXNjYWxlIGluZGljYXRvcnMsIHBvc3RlcmlvciBzdXBwb3J0IGFuZCA5NSUgSFBEIGJhcnMKcGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfSFBEIDwtIGZ1bmN0aW9uKG15LmJlYXN0LnRyZWUsIG15Lm1ldGFkYXRhLCBteS5waGUubWV0YSwgbXJzZC5mdWxsdHJlZSl7CiAgIyBnZXQgTVJDRCBmb3IgdHJlZQogIG1yc2QuQmVhc3QudHJlZS50ZXN0LnMgPC0gbWF4KGFzLm51bWVyaWModW5saXN0KG15Lm1ldGFkYXRhW215Lm1ldGFkYXRhJFNhbXBsZV9OYW1lICVpbiUgbXkuYmVhc3QudHJlZUBwaHlsbyR0aXAubGFiZWwsIlNhbXBsZV9ZZWFyIl0pKSkKICBtcnNkLkJlYXN0LnRyZWUudGVzdCA8LSBsdWJyaWRhdGU6OnltZChwYXN0ZTAobXJzZC5CZWFzdC50cmVlLnRlc3QucywiLTA2LTAxIikpIAogIG1yc2QuQmVhc3QudHJlZS5mdWxsdHJlZSA8LSBsdWJyaWRhdGU6OnltZChtcnNkLmZ1bGx0cmVlKSAKICAjbXJzZC5CZWFzdC50cmVlLnRlc3QKICAjIHBsb3QgYmFzaWMgdHJlZQogIG9wdGlvbnMoaWdub3JlLm5lZ2F0aXZlLmVkZ2U9VFJVRSkKICBwLlRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QgPC0gZ2d0cmVlKG15LmJlYXN0LnRyZWUsIG1yc2Q9bXJzZC5CZWFzdC50cmVlLnRlc3QsIGxhZGRlcml6ZSA9IFQsIHNpemU9MC40KSArIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDE5NjAsMjAyMCwxMCksIG1pbm9yX2JyZWFrcz1zZXEoMjAwMCwgMjAyMCwgMSkpICsKICAgIHRoZW1lX3RyZWUyKCkgKwogICAgIyBBZGQgZGF0ZSBsaW5lcyBmb3IgZWFzeSBpbnRlcnByZXRhdGlvbiAgCiAgICB0aGVtZShwYW5lbC5ncmlkLm1ham9yICAgPSBlbGVtZW50X2xpbmUoY29sb3I9ImdyZXk1MCIsIHNpemU9LjIpLAogICAgICAgICAgcGFuZWwuZ3JpZC5taW5vciAgID0gZWxlbWVudF9saW5lKGNvbG9yPSJncmV5ODUiLCBzaXplPS4yKSwKICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSkKICAjIEFkZCBwb3N0ZXJpb3Igc3VwcG9ydCBhcyBub2RlIHBvaW50cwogIHAuVFBBLmJlYXN0LnN1YnRyZWUudGVzdCA8LSBwLlRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QgKyBnZW9tX3BvaW50MihhZXMoc3Vic2V0PSghaXNUaXAgJiBhcy5udW1lcmljKHBvc3Rlcmlvcik+MC44KSksY29sb3I9ImdyYXk2MCIsc2l6ZT0yLGFscGhhPTAuNSwgc2hhcGU9MTgpICsgCiAgICBnZW9tX3BvaW50MihhZXMoc3Vic2V0PSghaXNUaXAgJiBhcy5udW1lcmljKHBvc3Rlcmlvcik+MC45MSkpLGNvbG9yPSJncmF5NDAiLHNpemU9MyxzaGFwZT0xOCxhbHBoYT0wLjUpICsgCiAgICBnZW9tX3BvaW50MihhZXMoc3Vic2V0PSghaXNUaXAgJiBhcy5udW1lcmljKHBvc3Rlcmlvcik+PTAuOTYpKSxjb2xvcj0iYmxhY2siLHNpemU9MyxzaGFwZT0xOCxhbHBoYT0wLjUpCiAgIyMjIyMjCiAgIyBleHRyYWN0IDk1JSBIUEQgaW50ZXJ2YWxzIC0gZ2VvbV9yYW5nZSBzZWVtcyB1bmFibGUgdG8gZG8gY29ycmVjdGx5IHdpdGggdGhpcyB0cmVlIChrbm93biBidWcgZm9yIHRpcCBkYXRlZCB0cmVlcyksIHNvIGV4dHJhY3QgZGF0YSBhbmQgcGxvdCB1c2luZyBnZW9tX3NlZ21lbnQKICBUUEEuYmVhc3Quc3VidHJlZS50ZXN0LmRhdGEgPC0gZm9ydGlmeShteS5iZWFzdC50cmVlKQogIG1pbm1heCA8LSB0KG1hdHJpeCh1bmxpc3QoVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5kYXRhWyFpcy5uYShUUEEuYmVhc3Quc3VidHJlZS50ZXN0LmRhdGEkaGVpZ2h0XzAuOTVfSFBEKSwiaGVpZ2h0XzAuOTVfSFBEIl0pLG5yb3c9MikpCiAgYmFyX2RmIDwtIGRhdGEuZnJhbWUobm9kZV9pZD1UUEEuYmVhc3Quc3VidHJlZS50ZXN0LmRhdGFbIWlzLm5hKFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZGF0YSRoZWlnaHRfMC45NV9IUEQpLCJub2RlIl0sYXMuZGF0YS5mcmFtZShtaW5tYXgpKQogIG5hbWVzKGJhcl9kZikgPC0gYygnbm9kZV9pZCcsJ21pbicsJ21heCcpIAogIGJhcl9kZiA8LSBiYXJfZGYgJT4lIGZpbHRlcihub2RlX2lkID4gTnRpcChteS5iZWFzdC50cmVlQHBoeWxvKSkKICBiYXJfZGYgPC0gYmFyX2RmICU+JSBsZWZ0X2pvaW4oVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5kYXRhLCBieT1jKCdub2RlX2lkJz0nbm9kZScpKSAjJT4lIHNlbGVjdChub2RlX2lkLG1pbixtYXgseSkKICAjbXJjZC5kZWNpbWFsIDwtIGRlY2ltYWxfZGF0ZShtcnNkLkJlYXN0LnRyZWUudGVzdCkKICBtcmNkLmRlY2ltYWwgPC0gZGVjaW1hbF9kYXRlKG1yc2QuQmVhc3QudHJlZS5mdWxsdHJlZSkKICAKICAjIE5vdyBhZGQgSFBEcyB0byBwbG90CiAgcC5UUEEuYmVhc3Quc3VidHJlZS50ZXN0IDwtIHAuVFBBLmJlYXN0LnN1YnRyZWUudGVzdCArIGdlb21fc2VnbWVudChhZXMoeD1tcmNkLmRlY2ltYWwtbWF4LCB5PXksIHhlbmQ9bXJjZC5kZWNpbWFsLW1pbiwgeWVuZD15KSwgZGF0YT1iYXJfZGYsIGNvbG9yPSdyZWQnLCBhbHBoYT0wLjIsIHNpemU9Mi4wKQogICMgT3V0cHV0IHRyZWUgCiAgcmV0dXJuKHAuVFBBLmJlYXN0LnN1YnRyZWUudGVzdCkKfQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBGdW5jdGlvbiB0byBhZGQgbWV0YWRhdGEgdG8gdHJlZQojIEhhcyB0d28gb3B0aW9uYWwgYXJndW1lbnRzICJpbml0aWFsLnRyYWNrLm9mZnNldCIgYW5kICJ0cmFjay5zY2FsaW5nIiB3aGljaCBjYW4gYmUgdXNlZCB0byBhbHRlciB0aGUgd2lkdGggYW5kIHBvc2l0aW9uaW5nIG9mIG1ldGFkYXRhIHRyYWNrcwoKcGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfUEhFX21ldGFkYXRhIDwtIGZ1bmN0aW9uKG15LmJlYXN0LnRyZWUuaW5wdXQsIG15Lm1ldGFkYXRhLCBteS5waGUubWV0YSwgaW5pdGlhbC50cmFjay5vZmZzZXQsIHRyYWNrLnNjYWxpbmcpewogICAgIyBBZGQgY29kZSB0byBhbGxvdyBzY2FsaW5nIHVwIG9mIHRoZSB0cmFjayBvZmZzZXRzIGFuZCB3aWR0aHMgLSB1c2VmdWwgZm9yIG11Y2ggYmlnZ2VyIGxlbmd0aCB0cmVlcwogIGlmKG1pc3NpbmcoaW5pdGlhbC50cmFjay5vZmZzZXQpKXsKICAgIGluaXRpYWwudHJhY2sub2Zmc2V0IDwtIDAKICB9ICAgIAogIGlmKG1pc3NpbmcodHJhY2suc2NhbGluZykpewogICAgdHJhY2suc2NhbGluZyA8LSAxCiAgfQogICMgQ2FsY3VsYXRlIGFtb3VudCB0byBvZmZzZXQgZWFjaCBoZWF0bWFwIHRyYWNrCiAgb2Zmc2V0LmRpc3QgPC0gNCp0cmFjay5zY2FsaW5nCiAgdHJhY2sud2lkdGggPC0gKDEvbWF4KG15LmJlYXN0LnRyZWUuaW5wdXQkZGF0YSRoZWlnaHQpKjMpKnRyYWNrLnNjYWxpbmcKICAKICAjIG1ha2UgYSBsaXN0IG9mIHRheGEgdXNlZCBpbiB0aGlzIHBsb3QgCiAgbXkudGF4YS5saXN0IDwtIGFzLmNoYXJhY3Rlcih1bmxpc3QoZmlsdGVyKG15LmJlYXN0LnRyZWUuaW5wdXQkZGF0YSwgaXNUaXA9PVRSVUUpICU+JSBzZWxlY3QobGFiZWwpKSkKICAKICAjIG1ha2UgYSBjb2xvciBzY2FsZSBmb3Igc2FtcGxpbmcgeWVhcnMKICAjUEhFLnN1YmxpbnRlc3QueWVhci5jb2xzIDwtIGRhdGEuZnJhbWUoeWVhcj1zb3J0KHVuaXF1ZShhcy5udW1lcmljKHVubGlzdChteS5tZXRhZGF0YVsobXkubWV0YWRhdGEkU2FtcGxlX05hbWUgJWluJSBteS50YXhhLmxpc3QpLCJTYW1wbGVfWWVhciJdLHVzZS5uYW1lcz1GKSkpKSxzdHJpbmdzQXNGYWN0b3JzID0gVCkKICAjUEhFLnN1YmxpbnRlc3QueWVhci5jb2xzJHllYXIuY29scyA8LSBjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwoNywgIllsT3JSZCIpKShucm93KFBIRS5zdWJsaW50ZXN0LnllYXIuY29scykpCiAgCiAgIyBPciBhbHRlcm5hdGl2ZWx5LCB1c2UgYSBjb21tb24gY29sb3VyIHNjaGVtZSBmb3IgYWxsIGRhdGEgKG1heWJlIG1vcmUgc2Vuc2libGUpCiAgUEhFLnN1YmxpbnRlc3QueWVhci5jb2xzIDwtIGRhdGEuZnJhbWUoeWVhcj1UUEEueWVhci5jdXR0b2ZmLmNvbHMkZGF0ZS5jdXR0b2ZmLCB5ZWFyLmNvbHM9VFBBLnllYXIuY3V0dG9mZi5jb2xzJGRhdGUuY3V0dG9mZi5jb2wsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogIAogICMgbWFrZSBtZXRhZGF0YSBmaWxlIGZvciBVSyByZWdpb25zIHByZXNlbnQgaW4gc3VibGluZWFnZQogIHN1Ymxpbi50ZXN0LnJlZ2lvbi5tZXRhIDwtIGRhdGEuZnJhbWUocm93Lm5hbWVzPWFzLmNoYXJhY3Rlcih1bmxpc3QobXkucGhlLm1ldGFbbXkucGhlLm1ldGEkU2FtcGxlX05hbWUgJWluJSBteS50YXhhLmxpc3QsIlNhbXBsZV9OYW1lIl0pKSwgUmVnaW9uPWFzLmNoYXJhY3Rlcih1bmxpc3QobXkucGhlLm1ldGFbbXkucGhlLm1ldGEkU2FtcGxlX05hbWUgJWluJSBteS50YXhhLmxpc3QsInBoZV9jZW50cmUiXSkpLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKICAKICAjIEFkZCBoZWF0bWFwIHN0cmlwcwogICMgU2FtcGxlIFllYXIKICAjVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgPC0gZ2hlYXRtYXAobXkuYmVhc3QudHJlZS5pbnB1dCwgVFBBLnJhd3NlcS5hbGwuWWVhcnMucCwgY29sb3I9TlVMTCx3aWR0aD10cmFjay53aWR0aCwgb2Zmc2V0PWluaXRpYWwudHJhY2sub2Zmc2V0K29mZnNldC5kaXN0LGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKwogICAgI3NjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlllYXIiLCB2YWx1ZXM9UEhFLnN1YmxpbnRlc3QueWVhci5jb2xzJHllYXIuY29scyxicmVha3M9UEhFLnN1YmxpbnRlc3QueWVhci5jb2xzJHllYXIsIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMSwgbmNvbD0yKSkgKwogICAgI2dnbmV3c2NhbGU6Om5ld19zY2FsZV9maWxsKCkKICBUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCA8LSBnaGVhdG1hcChteS5iZWFzdC50cmVlLmlucHV0LCBUUEEucmF3c2VxLnllYXIuY3V0dG9mZi5wLCBjb2xvcj1OVUxMLHdpZHRoPXRyYWNrLndpZHRoLCBvZmZzZXQ9aW5pdGlhbC50cmFjay5vZmZzZXQrb2Zmc2V0LmRpc3QsY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsIGZvbnQuc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluKSArCiAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJZZWFyIiwgdmFsdWVzPVBIRS5zdWJsaW50ZXN0LnllYXIuY29scyR5ZWFyLmNvbHMsYnJlYWtzPVBIRS5zdWJsaW50ZXN0LnllYXIuY29scyR5ZWFyLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEsIG5jb2w9MikpICsKICAgIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9maWxsKCkKICAKICAjIEFkZCBjb3VudHJ5CiAgVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgPC0gZ2hlYXRtYXAoVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwsIFRQQS5yYXdzZXEuY291bnRyaWVzLnAsIGNvbG9yPU5VTEwsd2lkdGg9dHJhY2sud2lkdGgsIG9mZnNldD1pbml0aWFsLnRyYWNrLm9mZnNldCsob2Zmc2V0LmRpc3QqMiksY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsIGZvbnQuc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluKSArIAogICAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iQ291bnRyeSIsIHZhbHVlcz1jb250aW5lbnRhbC5jb3VudHJ5LmNvbHMuYnJldzIkY291bnRyeS5jb2wsIGJyZWFrcz1jb250aW5lbnRhbC5jb3VudHJ5LmNvbHMuYnJldzIkR2VvX0NvdW50cnksIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMikpICsKICAgIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9maWxsKCkKICAjIFVLIG9yIG5vbi1VSwogIFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZ2xvYmFsLnBsb3QxLnJlZ2lvbmFsIDwtIGdoZWF0bWFwKFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZ2xvYmFsLnBsb3QxLnJlZ2lvbmFsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRQQS5yYXdzZXEuVUsucCwgY29sb3I9TlVMTCx3aWR0aD10cmFjay53aWR0aCxvZmZzZXQ9aW5pdGlhbC50cmFjay5vZmZzZXQrKG9mZnNldC5kaXN0KjMpLCBjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCxmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkVuZ2xhbmQvT3RoZXIiLCBicmVha3M9YygiRW5nbGFuZCIsIk90aGVyIiksIHZhbHVlcz1jKCJibGFjayIsImdyZXk5NSIpLCBuYS52YWx1ZSA9ICJ3aGl0ZSIsIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMywgbmNvbD0yKSkgKwogICAgZ2duZXdzY2FsZTo6bmV3X3NjYWxlX2ZpbGwoKQogICMgVUsgUEhFIHJlZ2lvbgogIFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZ2xvYmFsLnBsb3QxLnJlZ2lvbmFsIDwtIGdoZWF0bWFwKFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZ2xvYmFsLnBsb3QxLnJlZ2lvbmFsLCBzdWJsaW4udGVzdC5yZWdpb24ubWV0YSwgY29sb3I9TlVMTCx3aWR0aD10cmFjay53aWR0aCwgb2Zmc2V0PWluaXRpYWwudHJhY2sub2Zmc2V0KyhvZmZzZXQuZGlzdCo0KSxjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCwgZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsgCiAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJVS0hTQSBSZWdpb24iLCB2YWx1ZXM9UEhFLnJlZ2lvbi5jb2xzLmJyZXckcmVnaW9uLmNvbCwgYnJlYWtzPVBIRS5yZWdpb24uY29scy5icmV3JFVLSFNBLnJlZ2lvbiwgbmEudmFsdWUgPSAid2hpdGUiLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDQpKSArCiAgICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpCiAgCiAgIyBUUEEgc3VibGluZWFnZQogICNUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCA8LSBnaGVhdG1hcChUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCwgZGF0YS5mcmFtZShyb3cubmFtZXM9VFBBLm1ldGEyLjEkU2FtcGxlX05hbWUsIFN1YmxpbmVhZ2U9VFBBLm1ldGEyLjEkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSwgY29sb3I9TlVMTCx3aWR0aD10cmFjay53aWR0aCxvZmZzZXQ9aW5pdGlhbC50cmFjay5vZmZzZXQrKG9mZnNldC5kaXN0KjUpLCBjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCxmb250LnNpemU9Mi41KSArIAogICNzY2FsZV9maWxsX21hbnVhbChuYW1lPSJTdWJsaW5lYWdlIix2YWx1ZXM9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UuY29scywgYnJlYWtzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDUpKSAKICAKICBUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCA8LSBUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCArIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0ncmlnaHQnKSArCiAgICBuZXdfc2NhbGVfZmlsbCgpICsKICAgIGdlb21fcm9vdGVkZ2UoMikgKwogICAgTlVMTAogIAogICMgY2FsY3VsYXRlIG51bWJlciBvZiB0YXhhCiAgdGVzdC50YXhhY291bnQgPC0gbGVuZ3RoKG15LnRheGEubGlzdCkKICAjIEFkanVzdCBmaW5hbCBwbG90IHggYW5kIHkgYXhpcyB0byBtYWtlIHNwYWNlIGZvciBsYWJlbHMgdXNpbmcgdGF4YSBjb3VudHMKICB4LmF4aXMubGltaXRzIDwtIGdncGxvdF9idWlsZChUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCkkbGF5b3V0JHBhbmVsX3NjYWxlc194W1sxXV0kcmFuZ2UkcmFuZ2UKICBUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCA8LSBUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCArIAogICAgY29vcmRfY2FydGVzaWFuKHk9YygtMC41LSh0ZXN0LnRheGFjb3VudC8xNSksdGVzdC50YXhhY291bnQrMiksIHg9Yyh4LmF4aXMubGltaXRzWzFdLHguYXhpcy5saW1pdHNbMl0rMykpCiAgCiAgcmV0dXJuKFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZ2xvYmFsLnBsb3QxLnJlZ2lvbmFsKQp9CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKYGBgCgpcCkdyZWF0LCBub3cgbGV0J3MgcGxvdCBhIGZ1bGwgYmVhc3QgdHJlZQpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQojIGZ1bmN0aW9uIGZvciB4LWF4aXMgdGltZSBicmVha3MgbmVlZHMgdHdlYWtpbmcgZm9yIHRoZSBmdWxsIHRyZWUKVFBBLkdsb2JhbC5mdWxsLkJlYXN0VHJlZS51a21ldGEgPC0gcGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfUEhFX21ldGFkYXRhKHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX0hQRChteS5iZWFzdC50cmVlID0gZnVsbC5iZWFzdDIudHJlZSwgbXkubWV0YWRhdGEgPSBUUEEubWV0YTIuMSwgbXkucGhlLm1ldGEgPSBQSEUubWV0YWRhdGEubGlua2VkLCBtcnNkLmZ1bGx0cmVlID0gIjIwMTktMDYtMDEiKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDE0MDAsMjAyMCw1MCksIG1pbm9yX2JyZWFrcz1zZXEoMTk1MCwgMjAyMCwgNSkpLCBteS5tZXRhZGF0YSA9IFRQQS5tZXRhMi4xLCBteS5waGUubWV0YSA9IFBIRS5tZXRhZGF0YS5saW5rZWQsIHRyYWNrLnNjYWxpbmcgPSA1KQoKVFBBLkdsb2JhbC5mdWxsLkJlYXN0VHJlZS51a21ldGEKCiNnZ3NhdmUocGFzdGUwKEZpZ3VyZV9vdXRwdXRfZGlyZWN0b3J5LCJTdXBGaWc3X1RQQV9GdWxsQmVhc3RUcmVlLiIsZm9ybWF0KFN5cy5EYXRlKCksIiVZJW0lZCIpLCIucGRmIiksIHVuaXRzPSdtbScsIHdpZHRoPTE4NSwgaGVpZ2h0PTI0MCwgZGV2aWNlPSdwZGYnLCBkcGk9MTIwMCkKYGBgCgpcCk5vdyBkbyBzdWJsaW5lYWdlIHBsb3RzClwKTWFrZSBzb21lIHBsb3RzCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQojIFN1YmxpbmVhZ2UgMQpzdWJsaW5lYWdlLjEudHJlZS5oZWF0bWFwIDwtIHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX1BIRV9tZXRhZGF0YShwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9IUEQoRXh0cmFjdF9zdWJsaW5lYWdlX3RyZWVfZm9yX3Bsb3QoZnVsbC5iZWFzdDIudHJlZSwgVFBBLm1ldGEyLjEsIFBIRS5tZXRhZGF0YS5saW5rZWQsIG15LnN1YmxpbmVhZ2UgPSAxKSwgVFBBLm1ldGEyLjEsIFBIRS5tZXRhZGF0YS5saW5rZWQsIjIwMTktMDYtMDEiKSwgVFBBLm1ldGEyLjEsIFBIRS5tZXRhZGF0YS5saW5rZWQsIHRyYWNrLnNjYWxpbmcgPSAxLjIpCgojIFN1YmxpbmVhZ2UuMgpzdWJsaW5lYWdlLjIudHJlZS5oZWF0bWFwIDwtIHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX1BIRV9tZXRhZGF0YShwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9IUEQoRXh0cmFjdF9zdWJsaW5lYWdlX3RyZWVfZm9yX3Bsb3QoZnVsbC5iZWFzdDIudHJlZSwgVFBBLm1ldGEyLjEsIFBIRS5tZXRhZGF0YS5saW5rZWQsIG15LnN1YmxpbmVhZ2UgPSAyKSwgVFBBLm1ldGEyLjEsIFBIRS5tZXRhZGF0YS5saW5rZWQsIjIwMTktMDYtMDEiKSwgVFBBLm1ldGEyLjEsIFBIRS5tZXRhZGF0YS5saW5rZWQsIHRyYWNrLnNjYWxpbmcgPSAxKQoKIyBTdWJsaW5lYWdlLjgKc3VibGluZWFnZS44LnRyZWUuaGVhdG1hcCA8LSBwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9QSEVfbWV0YWRhdGEocGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfSFBEKEV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90KGZ1bGwuYmVhc3QyLnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBteS5zdWJsaW5lYWdlID0gOCksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCIyMDE5LTA2LTAxIiksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCB0cmFjay5zY2FsaW5nID0gMS4xKQoKIyBTdWJsaW5lYWdlLjE0CnN1YmxpbmVhZ2UuMTQudHJlZS5oZWF0bWFwIDwtIHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX1BIRV9tZXRhZGF0YShwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9IUEQoRXh0cmFjdF9zdWJsaW5lYWdlX3RyZWVfZm9yX3Bsb3QoZnVsbC5iZWFzdDIudHJlZSwgVFBBLm1ldGEyLjEsIFBIRS5tZXRhZGF0YS5saW5rZWQsIG15LnN1YmxpbmVhZ2UgPSAxNCksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCIyMDE5LTA2LTAxIiksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCB0cmFjay5zY2FsaW5nID0gMS4xKQoKYGBgCgpcClBsb3QgdG9nZXRoZXI/ClwKTWF5YmUgd2l0aCBzdWJsaW5lYWdlIDEgZXhwYW5kZWQ/CmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTJ9CnAuYmVhc3QudHJlZXMuaGVhdG1hcC5zdWJsaW5lYWdlcy5jb21iaS5vZmZzZXQxIDwtIHBsb3RfZ3JpZChzdWJsaW5lYWdlLjIudHJlZS5oZWF0bWFwLCAKICAgICAgICAgIHN1YmxpbmVhZ2UuOC50cmVlLmhlYXRtYXAsIAogICAgICAgICAgc3VibGluZWFnZS4xNC50cmVlLmhlYXRtYXAsIAogICAgICAgICAgbmNvbD0yLCBsYWJlbHM9YygiQiAtIFN1YmxpbmVhZ2UgMiIsIkMgLSBTdWJsaW5lYWdlIDgiLCJEIC0gU3VibGluZWFnZSAxNCIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplLCBzY2FsZT0wLjk1LCB2anVzdD0xLjApCgpwLmJlYXN0LnRyZWVzLmhlYXRtYXAuc3VibGluZWFnZXMuY29tYmkub2Zmc2V0MiA8LSBwbG90X2dyaWQoc3VibGluZWFnZS4xLnRyZWUuaGVhdG1hcCwgcC5iZWFzdC50cmVlcy5oZWF0bWFwLnN1YmxpbmVhZ2VzLmNvbWJpLm9mZnNldDEsIGxhYmVscz1jKCJBIC0gU3VibGluZWFnZSAxIiwgIiIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplLCBzY2FsZT0wLjk3NSwgbmNvbD0yLCByZWxfd2lkdGhzPWMoNiwxMSksIHZqdXN0PTIuNSkKCgpwLmJlYXN0LnRyZWVzLmhlYXRtYXAuc3VibGluZWFnZXMuY29tYmkub2Zmc2V0MgojZ2dzYXZlKHBhc3RlMChGaWd1cmVfb3V0cHV0X2RpcmVjdG9yeSwiU3VwRmlnOF9UUEEtUEhFX1N1YmxpbmVhZ2UtQmVhc3RUcmVlcy4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0yNjUsIGhlaWdodD0yMzAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCmBgYAoKXApOZWVkIHRvIGV4cGxvcmUgc3VibGluZWFnZSAxNCBhIGJpdCBtb3JlIHRvIGdldCBkYXRlcyBmb3IgdGhvc2Ugc3ViY2xhZGVzCmBgYHtyfQpzdWJsaW5lYWdlLjE0LnRyZWUuaGVhdG1hcCArIGdlb21fdGlwbGFiKHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgbGluZXNpemU9MC40KSAjMwpgYGAKClwKYGBge3J9CiMgT2ssIHRoZXJlIGFyZSBtdWx0aXBsZSBzdWJjbGFkZXMgaW4gdGhpcyB0cmVlCnN1YmxpbmVhZ2UuMTQudHJlZS5oZWF0bWFwLmRhdGEgPC0gc3VibGluZWFnZS4xNC50cmVlLmhlYXRtYXAkZGF0YQoKIyBnZXRNUkNBKGZ1bGwuYmVhc3QyLnRyZWVAcGh5bG8sYygiUEhFMTUwMTUwQSIsIk5MMTQiLCJUUEFfQkNDMTIyIiwiVFBBX0JDQzEyNiIsIlBIRTE0MDA3NkEiLCJUUEFfVUtCUkcwMDgiKSkgIDk4MgojIGZ1bGwuYmVhc3QyLnRyZWVAcGh5bG8kdGlwLmxhYmVsW3BoYW5nb3JuOjpEZXNjZW5kYW50cyhmdWxsLmJlYXN0Mi50cmVlQHBoeWxvLCA5ODIsIHR5cGUgPSBjKCJ0aXBzIikpW1sxXV1dCgpzdWJsaW5lYWdlLjE0Lmxvd2VyY2xhZGUubGlzdCA8LSBjKCJOTDE3IiwgIk5MMTkiLCAiUEhFMTQwMDg1QSIsICJQSEUxNDAwODlBIiwgIlBIRTE1MDExOEEiLCAiUEhFMTUwMTIxQSIsICJQSEUxNTAxMzNBIiwgIlBIRTE1MDE0M0EiLCAiUEhFMTUwMTQ1QSIsICJQSEUxNTAxNjJBIiwgIlBIRTE1MDE2NkEiLCAiUEhFMTUwMTY4QSIsICJQSEUxNjAyMjRBIiwgIlBIRTE2MDI0M0EiLCAiUEhFMTYwMjU1QSIsICJQSEUxNjAyNzZBIiwgIlBIRTE2MDI5MEEiLCAiUEhFMTYwMzAyQSIsICJQSEUxNjAzMDZBIiwgIlBIRTE3MDMzM0EiLCAiUEhFMTcwMzQ5QSIsICJQSEUxNzAzNzRBIiwgIlBIRTE3MDM4MUEiLCAiUEhFMTcwNjY0QSIsICJUUEFfRVNCQ04wMDUiLCAiVFBBX1VLQklSMDMyIikKCnN1YmxpbmVhZ2UuMTQudXBwZXJjbGFkZS5saXN0IDwtIGMoIk5MMTQiLCAiUEhFMTQwMDc2QSIsICJQSEUxNTAxNDlBIiwgIlBIRTE1MDE1MEEiLCAiUEhFMTUwMTcwQSIsICJQSEUxNjAxOTZBIiwgIlBIRTE2MDI2M0EiLCAiUEhFMTYwMjc0QSIsICJQSEUxNjAyODdBIiwgIlBIRTE2MDI5NEEiLCAiUEhFMTYwMzE2QSIsICJQSEUxNjAzMTdBIiwgIlBIRTE3MDM3MkEiLCAiUEhFMTcwMzg2QSIsICJQSEUxNzAzOTdBIiwgIlBIRTE3MDQwNUEiLCAiVFBBX0JDQzA4MSIsICJUUEFfQkNDMDg4IiwgIlRQQV9CQ0MwODkiLCAiVFBBX0JDQzEwMSIsICJUUEFfQkNDMTIyIiwgIlRQQV9CQ0MxMjYiLCAiVFBBX0JDQzEzNiIsICJUUEFfQkNDMTY5IiwgIlRQQV9IVU4xODAwMDQiLCAiVFBBX0hVTjE5MDAyMCIsICJUUEFfVUtCSVIwNDQiLCAiVFBBX1VLQlJHMDA3IiwgIlRQQV9VS0JSRzAwOCIpCgojIEdldCBNUkNBIGRhdGUgZm9yIGxvd2VyIGNsYWRlCnN1YmxpbmVhZ2UuMTQubG93ZXJjbGFkZS5saXN0LnRtcmNhIDwtIHN1YmxpbmVhZ2UuMTQudHJlZS5oZWF0bWFwLmRhdGFbc3VibGluZWFnZS4xNC50cmVlLmhlYXRtYXAuZGF0YSRub2RlPT1nZXRNUkNBKEV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90KGZ1bGwuYmVhc3QyLnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBteS5zdWJsaW5lYWdlID0gMTQpQHBoeWxvLCBzdWJsaW5lYWdlLjE0Lmxvd2VyY2xhZGUubGlzdCksIngiXQoKcGFzdGUwKCJUTVJDQSBmb3Igc3VibGluZWFnZSAxNCBsb3dlciBjbGFkZTogIixzdWJsaW5lYWdlLjE0Lmxvd2VyY2xhZGUubGlzdC50bXJjYSkKCiMgR2V0IE1SQ0EgZGF0ZSBmb3IgdXBwZXIgY2xhZGUKc3VibGluZWFnZS4xNC51cHBlcmNsYWRlLmxpc3QudG1yY2EgPC0gc3VibGluZWFnZS4xNC50cmVlLmhlYXRtYXAuZGF0YVtzdWJsaW5lYWdlLjE0LnRyZWUuaGVhdG1hcC5kYXRhJG5vZGU9PWdldE1SQ0EoRXh0cmFjdF9zdWJsaW5lYWdlX3RyZWVfZm9yX3Bsb3QoZnVsbC5iZWFzdDIudHJlZSwgVFBBLm1ldGEyLjEsIFBIRS5tZXRhZGF0YS5saW5rZWQsIG15LnN1YmxpbmVhZ2UgPSAxNClAcGh5bG8sIHN1YmxpbmVhZ2UuMTQudXBwZXJjbGFkZS5saXN0KSwieCJdCgpwYXN0ZTAoIlRNUkNBIGZvciBzdWJsaW5lYWdlIDE0IHVwcGVyIGNsYWRlOiAiLHN1YmxpbmVhZ2UuMTQudXBwZXJjbGFkZS5saXN0LnRtcmNhKQpgYGAKXApcCkV4dHJhY3Qga2V5IGluZm9ybWF0aW9uIGZvciBzdWJsaW5lYWdlIDYgKHR3byBzYW1wbGVzKQpgYGB7cn0Kc3VibGluZWFnZS42LnRyZWUuaGVhdG1hcCA8LSBwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9QSEVfbWV0YWRhdGEocGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfSFBEKEV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90KGZ1bGwuYmVhc3QyLnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBteS5zdWJsaW5lYWdlID0gNiksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCIyMDE5LTA2LTAxIiksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkKQoKc3VibGluZWFnZS42LnRyZWUuaGVhdG1hcC5kYXRhIDwtIHN1YmxpbmVhZ2UuNi50cmVlLmhlYXRtYXAkZGF0YQoKIyBHZXQgTVJDQSBkYXRlIGZvciB1cHBlciBjbGFkZQpzdWJsaW5lYWdlLjYuYmVhc3R0cmVlLnRtcmNhIDwtIGFzLm51bWVyaWMoc3VibGluZWFnZS42LnRyZWUuaGVhdG1hcC5kYXRhW3N1YmxpbmVhZ2UuNi50cmVlLmhlYXRtYXAuZGF0YSRub2RlPT1nZXRNUkNBKEV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90KGZ1bGwuYmVhc3QyLnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBteS5zdWJsaW5lYWdlID0gNilAcGh5bG8sIGMoIlBIRTEzMDA0OEEiLCAiUEhFMTYwMjgzQSIpKSwiYnJhbmNoIl0pCgoKcGFzdGUwKCJUTVJDQSBmb3Igc3VibGluZWFnZSA2IHVwcGVyIGNsYWRlOiAiLHN1YmxpbmVhZ2UuNi5iZWFzdHRyZWUudG1yY2EpCmBgYAoKCgpcClwKIyMjIEV4dHJhY3Qgc2FtcGxlICYgcG9wdWxhdGlvbiBzdGF0aXN0aWNzIGZyb20gZGF0YXNldHMgZm9yIHVzZSBpbiBtYW51c2NyaXB0IHRleHQKXApEYXRhc2V0IGFuZCBHZW9ncmFwaGljYWwgZGlzdHJpYnV0aW9ucwpgYGB7cn0KIyBkYXRhc2V0IGNvdW50cwpwYXN0ZTAoIlRvdGFsIFVLIHNhbXBsZXMgaW4gY2xlYW5lZC9kZWR1cGxpY2F0ZWQgZGF0YXNldDogIixucm93KFBIRS5tZXRhZGF0YS5saW5rZWQpKQpwYXN0ZTAoIk9mIHdoaWNoOiAiLG5yb3coUEhFLm1ldGFkYXRhLmxpbmtlZFtQSEUubWV0YWRhdGEubGlua2VkJGlzLlBIRT09IlBIRSIsXSksIiBmcm9tIFBIRSBSZWYgbGFiIGF0IENvbGluZGFsZSIpCnBhc3RlMCgiT2Ygd2hpY2g6ICIsbnJvdyhQSEUubWV0YWRhdGEubGlua2VkW1BIRS5tZXRhZGF0YS5saW5rZWQkaXMuUEhFPT0iT3RoZXIiLF0pLCIgZnJvbSBvdGhlciBsYWJzIikKCiMgcHJvcG9ydGlvbiB3aXRoIGdlb2dyYXBoaWNhbCBkYXRhCnBhc3RlMCgiRnJvbSBVSyBzYW1wbGVzLCAiLCBucm93KFBIRS5tZXRhZGF0YS5saW5rZWRbKFBIRS5tZXRhZGF0YS5saW5rZWQkcGhlX2NlbnRyZSAlbm90aW4lIGMoIk5vdCBLbm93biIsIlVLIChub3QgRW5nbGFuZCkiKSksXSksIiB3ZXJlIGdyb3VwZWQgaW50byBvbmUgb2YgdGhlIDkgUEggcmVnaW9ucyIpCnBhc3RlMCgiRnJvbSBVSyBzYW1wbGVzLCAiLCBucm93KFBIRS5tZXRhZGF0YS5saW5rZWRbUEhFLm1ldGFkYXRhLmxpbmtlZCRwaGVfY2VudHJlPT0iVUsgKG5vdCBFbmdsYW5kKSIsXSksICIgd2VyZSByZWZlcnJlZCBmcm9tIG91dHNpZGUgRW5nbGFuZCIpCnBhc3RlMCgiRnJvbSBVSyBzYW1wbGVzLCAiLCBucm93KFBIRS5tZXRhZGF0YS5saW5rZWRbUEhFLm1ldGFkYXRhLmxpbmtlZCRwaGVfY2VudHJlPT0iTm90IEtub3duIixdKSwgIiBoYWQgdW5rbm93biByZWdpb24iKQoKIyBjb3VudHMgJiBmcmFjdGlvbnMgYnkgUEhFIHJlZ2lvbgpQSEUuZ2VvLmNvdW50CgpgYGAKXApHZW5kZXIgT3JpZW50YXRpb24gc3RhdHMKYGBge3J9ClBIRS5vcmllbnRhdGlvbi5jb3VudHMKUEhFLmdlby5vcmllbnRhdGlvbi5jb3VudHMKUEhFLmdlby5ISVYuY291bnRzClBIRS5zdWJsaW5lYWdlLm9yaWVudGF0aW9uLmNvdW50cwpQSEUuc3VibGluZWFnZS5BZ2UKYGBgCgpcClN1YmxpbmVhZ2UgRGlzdHJpYnV0aW9ucwpgYGB7cn0KUEhFLkxpbmVhZ2UuY291bnQKUEhFLnN1Ymxpbi5jb3VudApQSEUuZ2VvLnN1YmxpbmVhZ2UKYGBgCgpcCk1hY3JvbGlkZSByZXNpc3RhbmNlIHN0YXRzCmBgYHtyfQpVSy5tYWNyb2xpZGUucmVzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lCiAgZHBseXI6Omdyb3VwX2J5KEEyMDU4RywgQTIwNTlHKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50LmFsbGVsZT1uKCkpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5jb3VudD1zdW0oQ291bnQuYWxsZWxlKSwgcGVyYy5hbGxlbGU9cm91bmQoKENvdW50LmFsbGVsZS90b3RhbC5jb3VudCkqMTAwLDEpKQpVSy5tYWNyb2xpZGUucmVzCgpVSy5tYWNyb2xpZGUucmVzLnN1YmxpbiA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JQogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgQTIwNThHLCBBMjA1OUcpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQuYWxsZWxlPW4oKSkgJT4lCiAgZHBseXI6OnVuZ3JvdXAoKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuY291bnQ9c3VtKENvdW50LmFsbGVsZSksIHBlcmMuYWxsZWxlPXJvdW5kKChDb3VudC5hbGxlbGUvdG90YWwuY291bnQpKjEwMCwxKSkKVUsubWFjcm9saWRlLnJlcy5zdWJsaW4KCgojIENhbGN1bGF0ZSBsb25nIGZvcm0gZGYsIHdpdGggZGlmZmVyZW50IDIzUyBhbGxlbGVzIChBMjA1OEcsIEEyMDU5RywgV1QsIFVuY2VydGFpbikgdi5zLiBzdWJsaW5lYWdlClVLLm1hY3JvbGlkZS5yZXMuc3VibGluLmxvbmcgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUKICBtdXRhdGUoUmVzaXN0YW5jZS5hbGxlbGU9aWZlbHNlKEEyMDU4Rz09IlllcyIsICJBMjA1OEciLCBpZmVsc2UoQTIwNTlHPT0iWWVzIiwgIkEyMDU5RyIsIGlmZWxzZSgoQTIwNThHPT0iTm8iICYgQTIwNTlHPT0iTm8iKSwiV2lsZCBUeXBlIiwgIlVuY2VydGFpbiIpKSkpICU+JQogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgUmVzaXN0YW5jZS5hbGxlbGUpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQucGVyLnN1Ymxpbi5NYWNyb2xpZGVzPW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5zdWJsaW49c3VtKENvdW50LnBlci5zdWJsaW4uTWFjcm9saWRlcyksIAogICAgICAgICAgICAgICAgZnJhY3Rpb249Q291bnQucGVyLnN1Ymxpbi5NYWNyb2xpZGVzL3RvdGFsLnN1YmxpbikgJT4lCiAgI2RwbHlyOjp1bmdyb3VwKCkgJT4lCiAgZHBseXI6OmFycmFuZ2UoKFJlc2lzdGFuY2UuYWxsZWxlKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoY3VtX2ZyYWN0ID0gY3Vtc3VtKGZyYWN0aW9uKSkgJT4lCiAgZHBseXI6Om11dGF0ZShjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkgJT4lCiAgZHBseXI6Om11dGF0ZShSZXNpc3RhbmNlLmFsbGVsZSA9IGZhY3RvcihSZXNpc3RhbmNlLmFsbGVsZSwgbGV2ZWxzPXJldihjKCJBMjA1OEciLCAiQTIwNTlHIiwgIlVuY2VydGFpbiIsICJXaWxkIFR5cGUiKSkpKQoKIyBNYWtlIHBsb3Qgb2YgbWFjcm9saWRlIHJlc2lzdGFuY2UgYnkgc3VibGluZWFnZXMKcC5zdWJsaW4uTWFjcm9saWRlcy5oYmFycGxvdCA8LSBnZ3Bsb3QoVUsubWFjcm9saWRlLnJlcy5zdWJsaW4ubG9uZywgYWVzKENvdW50LnBlci5zdWJsaW4uTWFjcm9saWRlcywgeT1UUEEucGluZWNvbmUuc3VibGluZWFnZSwgZmlsbD1SZXNpc3RhbmNlLmFsbGVsZSkpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJNYWNyb2xpZGVcblJlc2lzdGFuY2VcbkFsbGVsZSIsdmFsdWVzPWMoImluZGlhbnJlZDIiLCAic3RlZWxibHVlMSIsImdyZXk1NSIsICJncmV5OTAiKSwgYnJlYWtzPWMoIkEyMDU4RyIsICJBMjA1OUciLCAiVW5jZXJ0YWluIiwgIldpbGQgVHlwZSIpKSArCiAgbGFicyh5PSJUUEEgU3VibGluZWFnZSIsIHg9IlByb3BvcnRpb24gd2l0aCBNYWNyb2xpZGUgUmVzaXN0YW5jZSBBbGxlbGUiKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5jb2w9MikpICsKICBnZW9tX3RleHQoZGF0YT1VSy5tYWNyb2xpZGUucmVzLnN1Ymxpbi5sb25nLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgeT1UUEEucGluZWNvbmUuc3VibGluZWFnZSxsYWJlbD1Db3VudC5wZXIuc3VibGluLk1hY3JvbGlkZXMpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikgKwogIE5VTEwKCnAuc3VibGluLk1hY3JvbGlkZXMuaGJhcnBsb3QKCgojIENvbWJpbmUgcGxvdCB3aXRoIHN1YmxpbmVhZ2UgY291bnQgYmFycwpwLnN1Ymxpbi5NYWNyb2xpZGVzLmhiYXJwbG90LmNvbWJpIDwtIHBsb3RfZ3JpZChwLnN1YmxpbmVhZ2UuaGJhcnBsb3QgKyBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobmNvbD0zKSksIHAuc3VibGluLk1hY3JvbGlkZXMuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBucm93PTEsIGFsaWduPVQsIGxhYmVscz1jKCJBIiwgIkIiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSkKCnAuc3VibGluLk1hY3JvbGlkZXMuaGJhcnBsb3QuY29tYmkKCiNnZ3NhdmUocGFzdGUwKEZpZ3VyZV9vdXRwdXRfZGlyZWN0b3J5LCJTdXBGaWc5X1RQQS1QSEVfU3VibGluLU1hY3JvbGlkZS1SZXMuIixmb3JtYXQoU3lzLkRhdGUoKSwiJVklbSVkIiksIi5wZGYiKSwgdW5pdHM9J21tJywgd2lkdGg9MTYwLCBoZWlnaHQ9MTIwLCBkZXZpY2U9J3BkZicsIGRwaT0xMjAwKQoKYGBgClwKXAoKIyMjIFBhaXJ3aXNlIFNOUCBhbmFseXNpcwpcCk9LLCB3YW50IHRvIGludmVzdGlnYXRlIHRoZSBkaWZmZXJlbnQgcGF0dGVybnMgb2JzZXJ2YWJsZSBmb3IgdGhlIE5vcnRoIEVhc3Qgb2YgRW5nbGFuZCAocGFsZSBibHVlKSBpbiBTdWJsaW5lYWdlIDEKXApNdWx0aXBsZSB3YXlzIHdlIGNhbiBkbyB0aGlzIC0gaW5jbHVkaW5nIFNOUCBkaXN0YW5jZXMgKGFsc28gbXVsdGlwbGUgd2F5cyB0byBkbyB0aGF0KQpcCmBgYHtyfQojIyMKI1VzZSBwaHlsb2dlbmV0aWMgZGlzdGFuY2UgZnJvbSB0aGUgU05QIHNjYWxlZCB0cmVlClRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0IDwtIGFwZTo6Y29waGVuZXRpYy5waHlsbyhUUEEucHlqYXIudHJlZS5zdWJzZXQudWspClRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0Lm1lbHQgPC0gZGF0YS5mcmFtZShUYXhhMT1yb3cubmFtZXMoVFBBLnB5amFyLnRyZWUuc3Vic2V0LnVrLmNvcGhlbmV0aWMuU05QLmRpc3QpLCBUUEEucHlqYXIudHJlZS5zdWJzZXQudWsuY29waGVuZXRpYy5TTlAuZGlzdCwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpICU+JSB0aWR5cjo6Z2F0aGVyKFRheGEyLCBEaXN0YW5jZS5QaHlsbywgLVRheGExKQojIFRheGEgQ29tcGFyaXNvbnMgbGFiZWwKVFBBLnB5amFyLnRyZWUuc3Vic2V0LnVrLmNvcGhlbmV0aWMuU05QLmRpc3QubWVsdCRUYXhhX2NvbWJpbmF0aW9uIDwtIHNhcHBseSgxOm5yb3coVFBBLnB5amFyLnRyZWUuc3Vic2V0LnVrLmNvcGhlbmV0aWMuU05QLmRpc3QubWVsdCksIGZ1bmN0aW9uICh4KSBwYXN0ZTAoc29ydChjKGFzLmNoYXJhY3RlcihUUEEucHlqYXIudHJlZS5zdWJzZXQudWsuY29waGVuZXRpYy5TTlAuZGlzdC5tZWx0JFRheGExW3hdKSxhcy5jaGFyYWN0ZXIoVFBBLnB5amFyLnRyZWUuc3Vic2V0LnVrLmNvcGhlbmV0aWMuU05QLmRpc3QubWVsdCRUYXhhMlt4XSkpKSxjb2xsYXBzZT0iX19fIikpCiMgTWVyZ2UgdG9nZXRoZXIKI1RQQS5XR1MuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0IDwtIGRwbHlyOjpsZWZ0X2pvaW4oVFBBLldHUy5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQsIFRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0Lm1lbHRbLGMoIlRheGFfY29tYmluYXRpb24iLCJEaXN0YW5jZS5QaHlsbyIpXSwgYnk9IlRheGFfY29tYmluYXRpb24iKQoKVFBBLldHUy5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQgPC0gVFBBLnB5amFyLnRyZWUuc3Vic2V0LnVrLmNvcGhlbmV0aWMuU05QLmRpc3QubWVsdAoKClRQQS5XR1MuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0IDwtIHVuaXF1ZShUUEEuV0dTLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdCkKYGBgClwKT2ssIG5vdyBicmluZyBpbiBzb21lIG1ldGFkYXRhIGFuZCBjb21wYXJpc29ucwpgYGB7cn0KIyBCcmluZyBpbiBhbmQgbWVyZ2UgbWV0YWRhdGEKUEhFLm1ldGEucGFpcndpc2UudDEgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZFssYygiU2FtcGxlX05hbWUiLCJ5ZWFyIiwicGhlX2NlbnRyZSIsImxvbmRvbiIsImdlbmRlcl9vcmllbnRhdGlvbiIsImhpdnBvcyIsImFnZV9ncm91cCIsInVrYm9ybiIsIlRQQS5waW5lY29uZS5zdWJsaW5lYWdlIiwgIlRQQV9MaW5lYWdlIiwiR2VvX0NvdW50cnkiLCJpcy5VSyIsImlzLlBIRSIsICJTYW1wbGVfWWVhciIsImRhdGUuZGVjaW1hbCIpXQoKY29sbmFtZXMoUEhFLm1ldGEucGFpcndpc2UudDEpIDwtIHBhc3RlMChjb2xuYW1lcyhQSEUubWV0YS5wYWlyd2lzZS50MSksIi50MSIpCmNvbG5hbWVzKFBIRS5tZXRhLnBhaXJ3aXNlLnQxKVsxXSA8LSAiVGF4YTEiClBIRS5tZXRhLnBhaXJ3aXNlLnQyIDwtIFBIRS5tZXRhZGF0YS5saW5rZWRbLGMoIlNhbXBsZV9OYW1lIiwieWVhciIsInBoZV9jZW50cmUiLCJsb25kb24iLCJnZW5kZXJfb3JpZW50YXRpb24iLCJoaXZwb3MiLCJhZ2VfZ3JvdXAiLCJ1a2Jvcm4iLCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIsICJUUEFfTGluZWFnZSIsIkdlb19Db3VudHJ5IiwiaXMuVUsiLCJpcy5QSEUiLCAiU2FtcGxlX1llYXIiLCJkYXRlLmRlY2ltYWwiKV0KY29sbmFtZXMoUEhFLm1ldGEucGFpcndpc2UudDIpIDwtIHBhc3RlMChjb2xuYW1lcyhQSEUubWV0YS5wYWlyd2lzZS50MiksIi50MiIpCmNvbG5hbWVzKFBIRS5tZXRhLnBhaXJ3aXNlLnQyKVsxXSA8LSAiVGF4YTIiCgpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEgPC0gcGx5cjo6am9pbihUUEEuV0dTLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdCxQSEUubWV0YS5wYWlyd2lzZS50MSwgYnk9IlRheGExIiwgdHlwZT0ibGVmdCIpIApQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEgPC0gcGx5cjo6am9pbihQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEsUEhFLm1ldGEucGFpcndpc2UudDIsIGJ5PSJUYXhhMiIsIHR5cGU9ImxlZnQiKQoKIyBFeGNsdWRlIG1pc3NpbmcgZGF0YSAoZS5nLiBtaXNzaW5nIHN1YmxpbmVhZ2UpIC0gdGhpcyB3aWxsIGFsc28gcmVtb3ZlIG5vbi1VSyBzYW1wbGVzLCBzaW5jZSBmdWxsIG1ldGFkYXRhIGlzIG1pc3NpbmcgaGVyZQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhWyFpcy5uYShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UudDEpLF0KUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YVshaXMubmEoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLnQyKSxdCgpgYGAKClwKRGVmaW5lIGNvbXBhcmlzb25zCmBgYHtyfQojIFNhbWUgc2FtcGxlClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLnNhbXBsZSA8LSBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRheGExPT1QSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkVGF4YTIsInNhbWUiLCAiZGlmZmVyZW50IikKCiMgWWVhcnMgYmV0d2VlbiBzYW1wbGVzClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSR5ZWFyLmRpc3RhbmNlIDwtIGFicyhhcy5udW1lcmljKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSR5ZWFyLnQxKSAtIGFzLm51bWVyaWMoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHllYXIudDIpKQoKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFNhbXBsZV9ZZWFyLmRpc3RhbmNlIDwtIGFicyhhcy5udW1lcmljKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRTYW1wbGVfWWVhci50MSkgLSBhcy5udW1lcmljKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRTYW1wbGVfWWVhci50MikpCgojIFllYXJzIGJldHdlZW4gZGVjaW1hbCBkYXRlIChtb3JlIHByZWNpc2UgdGVtcG9yYWwgZGlzdGFuY2UpClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2UgPC0gYWJzKGFzLm51bWVyaWMoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRhdGUuZGVjaW1hbC50MSkgLSBhcy5udW1lcmljKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkYXRlLmRlY2ltYWwudDIpKQoKIyBFcGlkZW1pb2xvZ2ljYWwgdGltZSBiZXR3ZWVuIC0gY2F0YWdvcmljYWwKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGVwaS50aW1lLmRpc3RhbmNlLmNhdCA8LSBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTwxLzEyLCJtb250aCIsIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZGVjaW1hbC5kYXRlLmRpc3RhbmNlPD0zLzEyLCAicXVhcnRlciIsIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZGVjaW1hbC5kYXRlLmRpc3RhbmNlPD02LzEyLCAiaGFsZiB5ZWFyIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTEsICIxIHllYXIiLGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZGVjaW1hbC5kYXRlLmRpc3RhbmNlPD0yLCAiMiB5ZWFycyIsIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZGVjaW1hbC5kYXRlLmRpc3RhbmNlPD0zLCAiMyB5ZWFycyIsIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZGVjaW1hbC5kYXRlLmRpc3RhbmNlPD00LCAiNCB5ZWFycyIsIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZGVjaW1hbC5kYXRlLmRpc3RhbmNlPD01LCAiNSB5ZWFycyIsICBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9NiwgIjYgeWVhcnMiLCI+NiB5ZWFycyIpKSkpKSkpKSkKClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRlcGkudGltZS5kaXN0YW5jZS5jYXQgPC0gZmFjdG9yKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRlcGkudGltZS5kaXN0YW5jZS5jYXQsIGxldmVscz1jKCJtb250aCIsICJxdWFydGVyIiwiaGFsZiB5ZWFyIiwiMSB5ZWFyIiwgIjIgeWVhcnMiLCAiMyB5ZWFycyIsICI0IHllYXJzIiwgIjUgeWVhcnMiLCAiNiB5ZWFycyIsICI+NiB5ZWFycyIpKQoKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGVwaS50aW1lLmRpc3RhbmNlLmNhdC55ZWFycyA8LSBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9MSwgIjAiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9MiwgIjEiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9MywgIjIiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9NCwgIjMiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9NSwgIjQiLCAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTYsICI1IiwiPjUiKSkpKSkpCgoKIyBTYW1lIGNvdW50cnkKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuY291bnRyeSA8LSBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJEdlb19Db3VudHJ5LnQxID09IFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRHZW9fQ291bnRyeS50MiwgInNhbWUiLCAiZGlmZmVyZW50IikKCiMgSXMgVUsKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGJvdGgudWsgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRpcy5VSy50MSA9PSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkaXMuVUsudDIsICJzYW1lIiwgImRpZmZlcmVudCIpCgojIElzIFBIRQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkYm90aC5QSEUgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRpcy5QSEUudDEgPT0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGlzLlBIRS50MiwgInNhbWUiLCAiZGlmZmVyZW50IikKCiMgU2FtZSBUUEEgTGluZWFnZSAoY2xlYW5lZCB1cCBjbGFzc2lmaWNhdGlvbnMpClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLlRQQS5MaW5lYWdlIDwtIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkVFBBX0xpbmVhZ2UudDE9PVBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUUEFfTGluZWFnZS50MiwgInNhbWUiLCAiZGlmZmVyZW50IikKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuVFBBLkxpbmVhZ2UgPC0gc2FwcGx5KDE6bnJvdyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEpLCBmdW5jdGlvbih4KSBpZmVsc2UoKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUUEFfTGluZWFnZS50MVt4XT09IjAiIHwgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRQQV9MaW5lYWdlLnQyW3hdPT0iMCIpLE5BLFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLlRQQS5MaW5lYWdlW3hdKSkKCiMgU2FtZSBUUEEgc3VibGluZWFnZQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5UUEEuUGluZWNvbmUuY2x1c3RlciA8LSBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLnQxPT1QSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UudDIsInNhbWUiLCAiZGlmZmVyZW50IikKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuVFBBLlBpbmVjb25lLmNsdXN0ZXIgPC0gc2FwcGx5KDE6bnJvdyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEpLCBmdW5jdGlvbih4KSBpZmVsc2UoKChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5zYW1wbGVbeF09PSJkaWZmZXJlbnQiICYgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLnQxW3hdPT0iU2luZ2xldG9uIikgfChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5zYW1wbGVbeF09PSJkaWZmZXJlbnQiICYgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLnQyW3hdPT0iU2luZ2xldG9uIikpLCJkaWZmZXJlbnQiLFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLlRQQS5QaW5lY29uZS5jbHVzdGVyW3hdKSkKCiMgRGVmaW5lIEdlbmV0aWMgcmVsYXRpb25zaGlwcyBoaWVyYXJjaGljYWxseQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZ2Vub21pYy5jbHVzdGVyLmhpZXJhcmNoeSA8LSBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJERpc3RhbmNlPT0wLCJaZXJvX1NOUHMiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuVFBBLlBpbmVjb25lLmNsdXN0ZXI9PSJzYW1lIiwiU2FtZSBTdWJsaW5lYWdlIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLlRQQS5MaW5lYWdlPT0ic2FtZSIsICJTYW1lIExpbmVhZ2UiLCJEaWZmZXJlbnQgTGluZWFnZSIpKSkKClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5vbWljLmNsdXN0ZXIuaGllcmFyY2h5LnBoIDwtIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkRGlzdGFuY2UuUGh5bG89PTAsIlplcm9fU05QcyIsIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5UUEEuUGluZWNvbmUuY2x1c3Rlcj09InNhbWUiLCJTYW1lIFN1YmxpbmVhZ2UiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuVFBBLkxpbmVhZ2U9PSJzYW1lIiwgIlNhbWUgTGluZWFnZSIsIkRpZmZlcmVudCBMaW5lYWdlIikpKQoKCiMgU2FtZSBQSEUgcmVnaW9uClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLlBIRS5yZWdpb24gPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRwaGVfY2VudHJlLnQxPT1QSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkcGhlX2NlbnRyZS50MiwgInNhbWUiLCAiZGlmZmVyZW50IikKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFBIRS5jZW50cmUuY29tYmluYXRpb24gPC0gc2FwcGx5KDE6bnJvdyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEpLCBmdW5jdGlvbiAoeCkgcGFzdGUwKHNvcnQoYyhhcy5jaGFyYWN0ZXIoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHBoZV9jZW50cmUudDFbeF0pLGFzLmNoYXJhY3RlcihQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkcGhlX2NlbnRyZS50Mlt4XSkpKSxjb2xsYXBzZT0iX19fIikpCgojIGRvZXMgdGhlIGNvbWJpbmF0aW9uIGluY2x1ZGVkIExvbmRvbj8KUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGludm9sdmVzLkxvbmRvbiA8LSBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHBoZV9jZW50cmUudDE9PSJMb25kb24iIHwgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHBoZV9jZW50cmUudDI9PSJMb25kb24iLCAiTG9uZG9uIiwgIm5vdC1Mb25kb24iKQoKCiMgT3JpZW50YXRpb24gcGFpcgpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkT3JpZW50YXRpb25fY29tYmluYXRpb24gPC0gc2FwcGx5KDE6bnJvdyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEpLCBmdW5jdGlvbiAoeCkgcGFzdGUwKHNvcnQoYyhhcy5jaGFyYWN0ZXIoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50MVt4XSksYXMuY2hhcmFjdGVyKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDJbeF0pKSksY29sbGFwc2U9Il9fXyIpKQoKI1BIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRPcmllbnRhdGlvbi5DbGFzcyA8LSBzYXBwbHkoMTpucm93KFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSksIGZ1bmN0aW9uICh4KSBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50MVt4XT09Ik1TTSIgJiBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZ2VuZGVyX29yaWVudGF0aW9uLnQyW3hdPT0iTVNNIiwgIk1TTSIsCiMgICAgICAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF09PSJNU00iIHwgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50Mlt4XT09Ik1TTSIsICJNaXhlZCIsIAojICAgICAgICAgICAgICBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50MVt4XT09Ik1TVyIgJiBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZ2VuZGVyX29yaWVudGF0aW9uLnQyW3hdPT0iV1NNIiwiSGV0ZXJvc2V4dWFsIiwgCiMgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50MVt4XT09IldTTSIgJiBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZ2VuZGVyX29yaWVudGF0aW9uLnQyW3hdPT0iTVNXIiwiSGV0ZXJvc2V4dWFsIiwiVW5rbm93biIpKSkpKQoKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJE9yaWVudGF0aW9uLkNsYXNzIDwtIHNhcHBseSgxOm5yb3coUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhKSwgZnVuY3Rpb24gKHgpIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZ2VuZGVyX29yaWVudGF0aW9uLnQxW3hdPT0iR0JNU00iICYgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50Mlt4XT09IkdCTVNNIiwgIkdCTVNNIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50MVt4XSAlaW4lIGMoIk1TVyIsIldTTSIpICYgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50Mlt4XSAlaW4lIGMoIk1TVyIsIldTTSIpLCJIZXRlcm9zZXh1YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50MVt4XT09IkdCTVNNIiAmIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDJbeF0gJWluJSBjKCJNU1ciLCJXU00iKSwgIk1peGVkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50MVt4XSAlaW4lIGMoIk1TVyIsIldTTSIpICYgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50MVt4XT09IkdCTVNNIiwgIk1peGVkIiwgIlVua25vd24iKSkpKSkKICAgICAgICAgICAgICAgICAgICAKCgojIENvdW50cnkgQ29tcGFyaXNvbnMgbGFiZWwKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJENvdW50cnlfY29tYmluYXRpb25zIDwtIHBhc3RlMChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkR2VvX0NvdW50cnkudDEsIl9fXyIsUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJEdlb19Db3VudHJ5LnQyKQoKIyBTdWJzZXQgdG8gUEhFIGRhdGEgb25seSAoZWZmZWN0aXZlbHkgYWxyZWFkeSBkb25lLCBidXQgbGV0J3MgYmUgZXhwbGljaXQpClBIRS5UUEEuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhWyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkYm90aC51az09InNhbWUiICYgIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRib3RoLlBIRT09InNhbWUiKSxdClBIRS5UUEEuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEgPC0gUEhFLlRQQS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YVtQSEUuVFBBLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFBIRS5vbmx5PT0iUEhFIixdCgpQSEUuVFBBLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YVsoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGJvdGgudWs9PSJzYW1lIiksXQoKYGBgClwKXAoKYGBge3J9CiMgTWFrZSBzaW5nbGUgc2lkZWQKUEhFLlRQQS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSA8LSBQSEUuVFBBLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhWyFkdXBsaWNhdGVkKFBIRS5UUEEuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkVGF4YV9jb21iaW5hdGlvbiksXQoKYGBgCgoKXApcCiMjIyBQZXJmb3JtIGEgbW9yZSBkZXRhaWxlZCBhbmFseXNpcyBvZiBzYW1wbGVzIGZyb20gdGhlIE5vcnRoIEVhc3Qgb2YgRW5nbGFuZApcCkRvIGEgbW9yZSBkZXRhaWxlZCBleHBsb3JhdGlvbiBvZiB0aGUgTm9ydGggRWFzdCBvZiBFbmdsYW5kClwKYGBge3IsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTR9ClBIRS5tZXRhZGF0YS5saW5rZWQyLnJlZ2lvbl9Ob3J0aEVhc3QgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZFtQSEUubWV0YWRhdGEubGlua2VkJHBoZV9jZW50cmU9PSJOb3J0aCBFYXN0IixdCgojIENvbnN0cmFpbiBieSBzYW1wbGVzIGJlaW5nIGZyb20gdGhlIE5vcnRoIEVhc3QKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycyA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGFbKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRwaGVfY2VudHJlLnQxPT0iTm9ydGggRWFzdCIgJiBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5zYW1wbGU9PSJkaWZmZXJlbnQiKSxdCgojIENvbnN0cmFpbiBieSB0aGUgc2FtZSBQSEUgcmVnaW9uClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVyc1tQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzJHNhbWUuUEhFLnJlZ2lvbj09InNhbWUiLF0KCiNKdXN0IHBsb3QgdGhlc2UgZGlzdHJvcwpwLk5vcnRoRWFzdC5QYWlyd2lzZS5TTlBzLnVuY29uc3RyYWluZWQgPC0gZ2dwbG90KFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMsIGFlcyhEaXN0YW5jZS5QaHlsbykpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUudGV4dC5zaXplICsKICBsYWJzKHg9IlBhaXJ3aXNlIFNOUCBEaXN0YW5jZSIsIHk9IkNvbXBhcmlzb24gQ291bnQiKQoKcC5Ob3J0aEVhc3QuUGFpcndpc2UuU05Qcy51bmNvbnN0cmFpbmVkCmBgYAoKXApNYWtlIGEgc2luZ2xlIGxpbmthZ2UgbmV0d29yayBmcm9tIHRoZSBOb3J0aCBFYXN0IHNhbXBsZXMKYGBge3J9CgojIENvbnN0cmFpbiBieSBTTlAgZGlzdGFuY2UgKGxvb3NlciB0aGFuIHByZXZpb3VzbHkgLSB3ZSBqdXN0IHdhbnQgdG8gZmluZCBiYXNpYyBncm91cGluZ3Mgd2l0aGluIHN1YmxpbmVhZ2UgMSBmb3IgTkUgc2FtcGxlcykKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycyA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzW1BIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMkRGlzdGFuY2UuUGh5bG88PTIsXQoKIyBBbmQgbWFrZSBzdXJlIHRoYXQgd2UgYWN0dWFsbHkgaGF2ZSBnZW5ldGljIGRpc3RhbmNlIGRhdGEgZm9yIGFsbCBzYW1wbGVzIHdpdGhpbiB0aGUgbmV0d29yawpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnNbIWlzLm5hKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMkRGlzdGFuY2UuUGh5bG8pLF0KCiMgY2xlYW51cCBzb21lIGRhdGEgbm9pc2UKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycyA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzWyFpcy5uYShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzJHllYXIudDEpLF0KCiMgcHJlcGFyZSBpbnRwdXQgZGF0YSAod2l0aCBlZGdlIGluZm8pClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnNbLGMoIlRheGExIiwiVGF4YTIiLCJEaXN0YW5jZS5QaHlsbyIsImRlY2ltYWwuZGF0ZS5kaXN0YW5jZSIsInllYXIuZGlzdGFuY2UiLCJPcmllbnRhdGlvbi5DbGFzcyIsImVwaS50aW1lLmRpc3RhbmNlLmNhdCIpXQoKIyMjIyMjIyMjIyMjCiMgc29tZSBpc3N1ZXMgd2l0aCB1cGRhdGUgdG8gUjQgLSBkb3VibGUgc2lkZWQgbWF0cml4ClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxJGVkZ2VuYW1lIDwtIHNhcHBseSgxOm5yb3coUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycy5pbnB1dDEpLCBmdW5jdGlvbih4KSBwYXN0ZTAoc29ydChhcy5jaGFyYWN0ZXIodW5saXN0KFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxW3gsYygiVGF4YTEiLCJUYXhhMiIpXSkpKSxjb2xsYXBzZT0iX19fIikpClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxWyFkdXBsaWNhdGVkKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxJGVkZ2VuYW1lKSxdCgojIEFsc28gaGF2aW5nIGFuIGlzc3VlIHdpdGggdGF4YSBhcyBmYWN0b3JzIGhlcmUKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycy5pbnB1dDEkVGF4YTEgPC0gYXMuY2hhcmFjdGVyKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxJFRheGExKQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSRUYXhhMiA8LSBhcy5jaGFyYWN0ZXIoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycy5pbnB1dDEkVGF4YTIpCiMjIyMjIyMjIyMjIwoKI2ludmVyc2Ugd2VpZ2h0ClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxJERpc3RhbmNlLmludiA8LSAxL1BIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxJERpc3RhbmNlLlBoeWxvCgojIE1ha2UgYWN0dWFsIG5ldHdvcmsKc2V0LnNlZWQoMTIzNSkKUEhFLk5vcnRoRWFzdC5uZXR3b3JrIDwtIG5ldHdvcmsoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycy5pbnB1dDEsIG1hdHJpeC50eXBlID0gImVkZ2VsaXN0IiwgaWdub3JlLmV2YWwgPSBGQUxTRSwgZGlyZWN0ZWQgPSBGKQoKUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmdnIDwtIGdnbmV0d29yayhQSEUuTm9ydGhFYXN0Lm5ldHdvcmssIGxheW91dCA9ICJrYW1hZGFrYXdhaSIsIHdlaWdodHMgPSAiRGlzdGFuY2UuaW52IikKUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmdnJFRheGExIDwtIFBIRS5Ob3J0aEVhc3QubmV0d29yay5nZyR2ZXJ0ZXgubmFtZXMKCiMgZXh0cmFjdCB0ZW1wb3JhbCBjbHVzdGVycyBmcm9tIG5ldHdvcmsKUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmlnIDwtIGFzSWdyYXBoKFBIRS5Ob3J0aEVhc3QubmV0d29yaykKUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMgPC0gZGF0YS5mcmFtZShUYXhhMT1uZXR3b3JrLnZlcnRleC5uYW1lcyhQSEUuTm9ydGhFYXN0Lm5ldHdvcmspLCB2ZXJ0ZXgubm89YXMudmVjdG9yKFYoUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmlnKSksIGNsdXN0ZXI9aWdyYXBoOjpjb21wb25lbnRzKFBIRS5Ob3J0aEVhc3QubmV0d29yay5pZykkbWVtYmVyc2hpcCkKIyBGb3IgZWFzZSBvZiBzdG9yeSB0ZWxsaW5nIGluIHRoZSBwYXBlciwgZmxpcCBjbHVzdGVycyAyIGFuZCAzIGFyb3VuZCAoc28gd2UgY2FuIHRhbGsgYWJvdXQgMiBmaXJzdCkKUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMgPC0gUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMgJT4lCiAgZHBseXI6Om11dGF0ZShjbHVzdGVyLm9sZD1jbHVzdGVyLCBjbHVzdGVyPWlmZWxzZShjbHVzdGVyLm9sZD09MiwgMywgaWZlbHNlKGNsdXN0ZXIub2xkPT0zLDIsY2x1c3Rlci5vbGQpKSkKUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkQ2x1c3RlciA8LSBwYXN0ZTAoIkNsdXN0ZXIiLFBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJGNsdXN0ZXIpCgojIG1lcmdlIG1ldGFkYXRhIGJhY2sgaW4KUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmdnIDwtIHBseXI6OmpvaW4oUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmdnLCBkYXRhLmZyYW1lKFRheGExPVBIRS5tZXRhZGF0YS5saW5rZWQkU2FtcGxlX05hbWUsIFBIRS5tZXRhZGF0YS5saW5rZWRbLGMoInBoZV9jZW50cmUiLCJsb25kb24iLCJ5ZWFyIiwiYWdlX2dyb3VwIiwidWtib3JuIiwiZ2VuZGVyX29yaWVudGF0aW9uIiwiaGl2cG9zIiwiVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UiLCJUUEFfTGluZWFnZSIpXSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpLGJ5PSJUYXhhMSIsIHR5cGU9ImxlZnQiKQoKUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmdnIDwtIHBseXI6OmpvaW4oUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmdnLCBkYXRhLmZyYW1lKFRheGExPVBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJFRheGExLCBDbHVzdGVyPVBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJENsdXN0ZXIpLCBieT0iVGF4YTEiLCB0eXBlPSJsZWZ0IikKCmBgYApcClBsb3QgbmV0d29yawpgYGB7cn0KIyBQbG90IG5ldHdvcmsKcC5QSEUuTm9ydGhFYXN0Lm5ldHdvcmsuMlNOUCA8LSBnZ3Bsb3QoUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmdnLCBhZXMoeCA9IHgsIHkgPSB5LCB4ZW5kID0geGVuZCwgeWVuZCA9IHllbmQpKSArIAogIGdlb21fZWRnZXMoYWxwaGE9MC45MCwgY3VydmF0dXJlID0gMC4yLCBhZXMoY29sb3I9ZmFjdG9yKERpc3RhbmNlLlBoeWxvKSwgbGluZXR5cGU9ZmFjdG9yKERpc3RhbmNlLlBoeWxvKSkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImdyZXk1IiwiZ3JleTU1IiwiZ3JleTg1IiksIG5hbWU9IlNOUFxuRGlzdGFuY2UiKSArCiAgc2NhbGVfbGluZXR5cGUobmFtZT0iU05QXG5EaXN0YW5jZSIpICsKICB0aGVtZV9ibGFuaygpICsKICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfY29sb3IoKSArIGdnbmV3c2NhbGU6Om5ld19zY2FsZSgic2l6ZSIpICsKICBnZW9tX25vZGVsYWJlbChhZXMoY29sb3I9Z2VuZGVyX29yaWVudGF0aW9uLCBsYWJlbD1wYXN0ZShUYXhhMSx5ZWFyLHNlcD0iXG4iKSxmb250ZmFjZSA9ICJib2xkIiksIGFscGhhPTAuOCwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLTAuNCwgbGFiZWwuc2l6ZT0wLjE1LCBsYWJlbC5wYWRkaW5nID0gdW5pdCgwLjA1LCAibGluZXMiKSkgKwogIGdlb21fbm9kZXMoc2l6ZT0xLjAsIGFlcyhjb2xvcj1nZW5kZXJfb3JpZW50YXRpb24pKSArIAogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSJHZW5kZXJcbk9yaWVudGF0aW9uIiwgdmFsdWVzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uLmNvbHMsIGJyZWFrcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbikgKyAKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogIE5VTEwKcC5QSEUuTm9ydGhFYXN0Lm5ldHdvcmsuMlNOUAoKYGBgCgoKXApPaywgc28gdGhyZWUgbmV0d29ya3MuIENsZWFyIGRpZmZlcmVudGlhdGlvbiBvZiBhIGhldGVyb3NleHVhbCBuZXR3b3JrICh3aXRoIDAtc25wIGRpc3RhbmNlcykgYW5kIHR3byBwcmVkb21pbmFudGx5IE1TTSBuZXR3b3JrcwpcCkxldCdzIGxvb2sgYXQgdGhlIHBoeWxvZ2VuZXRpYyBjb250ZXh0IG9mIHRob3NlIE5vcnRoIEVhc3QgY2x1c3RlcnMgd2UndmUgZGVmaW5lZC4KUHVsbCBvdXQgc3VidHJlZXMgKGZyb20gc3VibGluZWFnZSAxIHN1YnRyZWUpClwKYGBge3IsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xMn0KIyBDbHVzdGVyIDEKQmVhc3QudHJlZS5ORS5jbHVzdGVyMSA8LSBnZXRNUkNBKGZ1bGwuYmVhc3QyLnRyZWVAcGh5bG8sIFBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzW1BIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJENsdXN0ZXI9PSJDbHVzdGVyMSIsIlRheGExIl0pCkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZSA8LSB0cmVlX3N1YnNldChmdWxsLmJlYXN0Mi50cmVlLCBub2RlPUJlYXN0LnRyZWUuTkUuY2x1c3RlcjEsIGxldmVsc19iYWNrPTApCgpwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZSA8LSBwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9QSEVfbWV0YWRhdGEocGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfSFBEKEJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZSwgVFBBLm1ldGEyLjEsIFBIRS5tZXRhZGF0YS5saW5rZWQsIjIwMTktMDYtMDEiKSwgVFBBLm1ldGEyLjEsIFBIRS5tZXRhZGF0YS5saW5rZWQsIGluaXRpYWwudHJhY2sub2Zmc2V0ID0gMTApCgojIENhbid0IGZpdCBpbiB0aXAgbGFicywgYnV0IHNpbmNlIHRoaXMgaXMgYSBwb2x5cGh5bGV0aWMgc3VidHJlZSwgaXQgd291bGQgYmUgaGVscGZ1bCB0byBhZGQgYSB0cmFjayB0byBoaWdobGlnaHQgdGhlIE5FIHN0cmFpbnMKUEhFLm1ldGFkYXRhLmxpbmtlZCRpcy5Ob3J0aEVhc3QgPC0gaWZlbHNlKFBIRS5tZXRhZGF0YS5saW5rZWQkcGhlX2NlbnRyZT09Ik5vcnRoIEVhc3QiLCJOb3J0aCBFYXN0IiwgIk90aGVyIEVuZ2xhbmQiKQpwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCA8LSBnaGVhdG1hcChwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZSwgZGF0YS5mcmFtZShyb3cubmFtZXM9UEhFLm1ldGFkYXRhLmxpbmtlZCRTYW1wbGVfTmFtZSwgYE5vcnRoIEVhc3RgPVBIRS5tZXRhZGF0YS5saW5rZWQkaXMuTm9ydGhFYXN0KSwgY29sb3I9TlVMTCx3aWR0aD0oMS9tYXgocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUkZGF0YSRoZWlnaHQpKjMpLCBvZmZzZXQ9MTArKDQqNSksY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsIGZvbnQuc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluKSArIAogICAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iTm9ydGggRWFzdFxuRW5nbGFuZCIsIHZhbHVlcz1jKCIjQTZDRUUzIiwiZ3JleTk1IiksIGJyZWFrcz1jKCJOb3J0aCBFYXN0IiwiT3RoZXIgRW5nbGFuZCIpLCBuYS52YWx1ZSA9ICJ3aGl0ZSIsIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gNSkpICsKICAgIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9maWxsKCkKCiMgSnVzdCBjb25maXJtIHRoZSBDbHVzdGVySURzIGZvciB0aGlzIHN1YnRyZWUgKG1ha2Ugc3VyZSBpdCBkb2Vzbid0IGVuY2xvc2Ugb3RoZXIgY2x1c3RlcnMpCnAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LndpdGhfY2x1c3RlcklEIDwtIGdoZWF0bWFwKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LCBkYXRhLmZyYW1lKHJvdy5uYW1lcz1QSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRUYXhhMSwgQ2x1c3RlcklEPVBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJENsdXN0ZXIpLCBjb2xvcj1OVUxMLHdpZHRoPSgxL21heChwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZSRkYXRhJGhlaWdodCkqMyksIG9mZnNldD0xMCsoNCo2KSxjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCwgZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsgCiAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJOb3J0aCBFYXN0XG5DbHVzdGVyIiwgdmFsdWVzPWMoIiM3ZmM5N2YiLCIjYmVhZWQ0IiwiI2ZkYzA4NiIpLCBicmVha3M9YygiQ2x1c3RlcjEiLCJDbHVzdGVyMiIsIkNsdXN0ZXIzIiksIG5hLnZhbHVlID0gIndoaXRlIiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSA2KSkgKwogICAgZ2duZXdzY2FsZTo6bmV3X3NjYWxlX2ZpbGwoKQoKIyBhZGQgYSBiaXQgbW9yZSByb29tIHRvIHRoZSB4IGF4aXMKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQueC5heGlzLmxpbWl0cyA8LSBnZ3Bsb3RfYnVpbGQocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQud2l0aF9jbHVzdGVySUQpJGxheW91dCRwYW5lbF9zY2FsZXNfeFtbMV1dJHJhbmdlJHJhbmdlCnAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LndpdGhfY2x1c3RlcklEIDwtIHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LndpdGhfY2x1c3RlcklEICsgCiAgICBjb29yZF9jYXJ0ZXNpYW4oeD1jKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LnguYXhpcy5saW1pdHNbMV0scC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQueC5heGlzLmxpbWl0c1syXSs0KSwgeT1jKC0wLjUtKGxlbmd0aCh1bmlxdWUocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQkZGF0YSRsYWJlbCkpLzE1KSxsZW5ndGgodW5pcXVlKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0JGRhdGEkbGFiZWwpKSsyKSkgKwogIHRoZW1lKGxlZ2VuZC5tYXJnaW4gPSBtYXJnaW4oLTAuNSwwLDAsMCwgdW5pdD0ibW0iKSkKI3AuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LndpdGhfY2x1c3RlcklECgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENsdXN0ZXIgMgpCZWFzdC50cmVlLk5FLmNsdXN0ZXIyIDwtIGdldE1SQ0EoZnVsbC5iZWFzdDIudHJlZUBwaHlsbywgUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHNbUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkQ2x1c3Rlcj09IkNsdXN0ZXIyIiwiVGF4YTEiXSkKQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlIDwtIHRyZWVfc3Vic2V0KGZ1bGwuYmVhc3QyLnRyZWUsIG5vZGU9QmVhc3QudHJlZS5ORS5jbHVzdGVyMiwgbGV2ZWxzX2JhY2s9MSkKCnAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlIDwtIHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX1BIRV9tZXRhZGF0YShwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9IUEQoQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwiMjAxOS0wNi0wMSIpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgaW5pdGlhbC50cmFjay5vZmZzZXQgPSAyMCkgKyBnZW9tX3RpcGxhYihzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGFsaWduPVQsIG9mZnNldD01LCBsaW5lc2l6ZT0wLjQpCiMgSnVzdCBhZGQgQ2x1c3RlcklEcyBmb3IgdGhpcyBzdWJ0cmVlIHRvIGhpZ2hsaWdodApwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZSA8LSBnaGVhdG1hcChwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZSwgZGF0YS5mcmFtZShyb3cubmFtZXM9UEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkVGF4YTEsIENsdXN0ZXJJRD1QSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRDbHVzdGVyKSwgY29sb3I9TlVMTCx3aWR0aD0oMS9tYXgocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUkZGF0YSRoZWlnaHQpKjMpLCBvZmZzZXQ9MjArKDQqNSksY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsIGZvbnQuc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluKSArIAogICAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iTm9ydGggRWFzdFxuQ2x1c3RlciIsIHZhbHVlcz1jKCIjN2ZjOTdmIiwiI2JlYWVkNCIsIiNmZGMwODYiKSwgYnJlYWtzPWMoIkNsdXN0ZXIxIiwiQ2x1c3RlcjIiLCJDbHVzdGVyMyIpLCBuYS52YWx1ZSA9ICJ3aGl0ZSIsIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gNSwgbmNvbD0yKSkgKwogICAgZ2duZXdzY2FsZTo6bmV3X3NjYWxlX2ZpbGwoKQojIGFkZCBhIGJpdCBtb3JlIHJvb20gdG8gdGhlIHggYXhpcwpwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZS54LmF4aXMubGltaXRzIDwtIGdncGxvdF9idWlsZChwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZSkkbGF5b3V0JHBhbmVsX3NjYWxlc194W1sxXV0kcmFuZ2UkcmFuZ2UKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUgPC0gcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUgKyAKICAgIGNvb3JkX2NhcnRlc2lhbih4PWMocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUueC5heGlzLmxpbWl0c1sxXSxwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZS54LmF4aXMubGltaXRzWzJdKzEyKSwgeT1jKC0wLjUtKGxlbmd0aCh1bmlxdWUocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUkZGF0YSRsYWJlbCkpLzIwKS0xLGxlbmd0aCh1bmlxdWUocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUkZGF0YSRsYWJlbCkpKzAuNSkpICsgCiAgdGhlbWUobGVnZW5kLm1hcmdpbiA9IG1hcmdpbigtMC41LDAsMCwwLCB1bml0PSJtbSIpKQoKI3AuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgQ2x1c3RlciAzCkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMgPC0gZ2V0TVJDQShmdWxsLmJlYXN0Mi50cmVlQHBoeWxvLCBQSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50c1tQSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRDbHVzdGVyPT0iQ2x1c3RlcjMiLCJUYXhhMSJdKQpCZWFzdC50cmVlLk5FLmNsdXN0ZXIzLnN1YnRyZWUgPC0gdHJlZV9zdWJzZXQoZnVsbC5iZWFzdDIudHJlZSwgbm9kZT1CZWFzdC50cmVlLk5FLmNsdXN0ZXIzLCBsZXZlbHNfYmFjaz0xKQoKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIzLnN1YnRyZWUgPC0gcGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfUEhFX21ldGFkYXRhKHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX0hQRChCZWFzdC50cmVlLk5FLmNsdXN0ZXIzLnN1YnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCIyMDE5LTA2LTAxIiksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBpbml0aWFsLnRyYWNrLm9mZnNldCA9IDI2KSArIGdlb21fdGlwbGFiKHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgYWxpZ249VCwgb2Zmc2V0PTMsIGxpbmVzaXplPTAuNCkKCiMgSnVzdCBhZGQgQ2x1c3RlcklEcyBmb3IgdGhpcyBzdWJ0cmVlIHRvIGhpZ2hsaWdodApwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZSA8LSBnaGVhdG1hcChwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZSwgZGF0YS5mcmFtZShyb3cubmFtZXM9UEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkVGF4YTEsIENsdXN0ZXJJRD1QSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRDbHVzdGVyKSwgY29sb3I9TlVMTCx3aWR0aD0oMS9tYXgocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIzLnN1YnRyZWUkZGF0YSRoZWlnaHQpKjMpLCBvZmZzZXQ9MjYrKDQqNSksY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsIGZvbnQuc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluKSArIAogICAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iTm9ydGggRWFzdFxuQ2x1c3RlciIsIHZhbHVlcz1jKCIjN2ZjOTdmIiwiI2JlYWVkNCIsIiNmZGMwODYiKSwgYnJlYWtzPWMoIkNsdXN0ZXIxIiwiQ2x1c3RlcjIiLCJDbHVzdGVyMyIpLCBuYS52YWx1ZSA9ICJ3aGl0ZSIsIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gNSwgbmNvbD0yKSkgKwogICAgZ2duZXdzY2FsZTo6bmV3X3NjYWxlX2ZpbGwoKQoKIyBhZGQgYSBiaXQgbW9yZSByb29tIHRvIHRoZSB4IGF4aXMKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIzLnN1YnRyZWUueC5heGlzLmxpbWl0cyA8LSBnZ3Bsb3RfYnVpbGQocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIzLnN1YnRyZWUpJGxheW91dCRwYW5lbF9zY2FsZXNfeFtbMV1dJHJhbmdlJHJhbmdlCnAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlIDwtIHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlICsgCiAgICBjb29yZF9jYXJ0ZXNpYW4oeD1jKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlLnguYXhpcy5saW1pdHNbMV0scC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIzLnN1YnRyZWUueC5heGlzLmxpbWl0c1syXSsxMiksIHk9YygtMC41LShsZW5ndGgodW5pcXVlKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlJGRhdGEkbGFiZWwpKS8yMCktMSxsZW5ndGgodW5pcXVlKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlJGRhdGEkbGFiZWwpKSswLjUpKSArIAogIHRoZW1lKGxlZ2VuZC5tYXJnaW4gPSBtYXJnaW4oLTAuNSwwLDAsMCwgdW5pdD0ibW0iKSkKI3AuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlCgojcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQud2l0aF9jbHVzdGVySUQKI3AuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlIAojcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIzLnN1YnRyZWUgCmBgYAoKXApTaW5jZSBDbHVzdGVyIDEgaXMgcmVhbGx5IHF1aXRlIHBvbHlwaHlsZXRpYywgaXQgbWF5YmUgbW9yZSB1c2VmdWwgdG8gc2hvdyB0aGUgY2x1c3RlcnMgaW4gY29udGV4dCBmb3IgdGhhdCBvbmUKCmBgYHtyfQojIEFkZCBOb3J0aCBFYXN0IGlkZW50aWZpZXIgY29sdW1uCnAuQmVhc3QudHJlZS5zdWJsaW5lYWdlMS5ORS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0IDwtIGdoZWF0bWFwKHN1YmxpbmVhZ2UuMS50cmVlLmhlYXRtYXAsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVBIRS5tZXRhZGF0YS5saW5rZWQkU2FtcGxlX05hbWUsIGBOb3J0aCBFYXN0YD1QSEUubWV0YWRhdGEubGlua2VkJGlzLk5vcnRoRWFzdCksIGNvbG9yPU5VTEwsd2lkdGg9KDEvbWF4KHN1YmxpbmVhZ2UuMS50cmVlLmhlYXRtYXAkZGF0YSRoZWlnaHQpKjMpKjEuMiwgb2Zmc2V0PTArKDQqNSkqMS4yLGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9Ik5vcnRoIEVhc3RcbkVuZ2xhbmQiLCB2YWx1ZXM9YygiI0E2Q0VFMyIsImdyZXk5NSIpLCBicmVha3M9YygiTm9ydGggRWFzdCIsIk90aGVyIEVuZ2xhbmQiKSwgbmEudmFsdWUgPSAid2hpdGUiLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDUpKSArCiAgICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpCgojIEp1c3QgY29uZmlybSB0aGUgQ2x1c3RlcklEcyBmb3IgdGhpcyBzdWJ0cmVlIChtYWtlIHN1cmUgaXQgZG9lc24ndCBlbmNsb3NlIG90aGVyIGNsdXN0ZXJzKQpwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCA8LSBnaGVhdG1hcChwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCwgZGF0YS5mcmFtZShyb3cubmFtZXM9UEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkVGF4YTEsIENsdXN0ZXJJRD1QSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRDbHVzdGVyKSwgY29sb3I9TlVMTCx3aWR0aD0oMS9tYXgocC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQkZGF0YSRoZWlnaHQpKjMpKjEuMiwgb2Zmc2V0PTArKDQqNikqMS4yLGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9Ik5vcnRoIEVhc3RcbkNsdXN0ZXIiLCB2YWx1ZXM9YygiIzdmYzk3ZiIsIiNiZWFlZDQiLCIjZmRjMDg2IiksIGJyZWFrcz1jKCJDbHVzdGVyMSIsIkNsdXN0ZXIyIiwiQ2x1c3RlcjMiKSwgbmEudmFsdWUgPSAid2hpdGUiLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDYsIG5jb2w9MikpICsKICAgIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9maWxsKCkKCiMgYWRkIGEgYml0IG1vcmUgcm9vbSB0byB0aGUgeCBheGlzCnAuQmVhc3QudHJlZS5zdWJsaW5lYWdlMS5ORS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LnguYXhpcy5saW1pdHMgPC0gZ2dwbG90X2J1aWxkKHAuQmVhc3QudHJlZS5zdWJsaW5lYWdlMS5ORS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0KSRsYXlvdXQkcGFuZWxfc2NhbGVzX3hbWzFdXSRyYW5nZSRyYW5nZQpwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCA8LSBwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCArIAogICAgY29vcmRfY2FydGVzaWFuKHg9YyhwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC54LmF4aXMubGltaXRzWzFdLHAuQmVhc3QudHJlZS5zdWJsaW5lYWdlMS5ORS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LnguYXhpcy5saW1pdHNbMl0rNCksIHk9YygtMC41LShsZW5ndGgodW5pcXVlKHAuQmVhc3QudHJlZS5zdWJsaW5lYWdlMS5ORS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0JGRhdGEkbGFiZWwpKS8xNSksbGVuZ3RoKHVuaXF1ZShwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCRkYXRhJGxhYmVsKSkrMikpCgojIHJlZHVjZSBzcGFjaW5nIGJldHdlZW4gbGVnZW5kIHNjYWxlcwpwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCA8LSBwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCArIHRoZW1lKGxlZ2VuZC5tYXJnaW4gPSBtYXJnaW4oLTAuOTUsMCwwLDAsIHVuaXQ9Im1tIikpCnAuQmVhc3QudHJlZS5zdWJsaW5lYWdlMS5ORS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0CgpgYGAKCgpcIApQbG90IHRvZ2V0aGVyCmBgYHtyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9CgpwLkJlYXN0LnRyZWUuTkUuc3VidHJlZXMuY29tYmkxIDwtIHBsb3RfZ3JpZChwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZSwgcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIzLnN1YnRyZWUsIG5jb2w9MSwgbGFiZWxzPWMoIkMgLSBDbHVzdGVyIDIiLCAiRCAtIENsdXN0ZXIgMyIpLCB2anVzdD0xLjAsIGxhYmVsX3NpemU9cGFuZWwubGFiLnNpemUsIHNjYWxlPTAuOTUpCgpwLkJlYXN0LnRyZWUuTkUuc3VidHJlZXMuY29tYmkyIDwtIHBsb3RfZ3JpZChwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC53aXRoX2NsdXN0ZXJJRCwgcC5CZWFzdC50cmVlLk5FLnN1YnRyZWVzLmNvbWJpMSwgbmNvbD0yLCByZWxfd2lkdGhzPWMoMywyKSwgbGFiZWxzPWMoIkIgLSBDbHVzdGVyIDEiLCAiIiksIGxhYmVsX3NpemU9cGFuZWwubGFiLnNpemUpCnAuQmVhc3QudHJlZS5ORS5zdWJ0cmVlcy5jb21iaTIKCgpwLkJlYXN0LnRyZWUuTkUuc3VidHJlZXMuY29tYmkzIDwtIHBsb3RfZ3JpZChwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCwgcC5CZWFzdC50cmVlLk5FLnN1YnRyZWVzLmNvbWJpMSwgbmNvbD0yLCByZWxfd2lkdGhzPWMoOCw3KSwgbGFiZWxzPWMoIkIgLSBTdWJsaW5lYWdlIDEgKEFsbCkiLCAiIiksIGxhYmVsX3NpemU9cGFuZWwubGFiLnNpemUsIHNjYWxlPTAuOTUsIHZqdXN0PTEuMCkKCnAuQmVhc3QudHJlZS5ORS5zdWJ0cmVlcy5jb21iaTMKCmBgYAoKXApcCkxvb2sgbW9yZSBjbG9zZWx5IGF0IHBvcHVsYXRpb24gZGVtb2dyYXBoaWNzIG9mIHRoZXNlIGNsdXN0ZXJzCmBgYHtyfQojIE1ldGFkYXRhIG9uIE5FIGNsdXN0ZXIgMgpQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6ZmlsdGVyKFNhbXBsZV9OYW1lICVpbiUgQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlQHBoeWxvJHRpcC5sYWJlbCkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KEdlb19Db3VudHJ5LCBpcy5Ob3J0aEVhc3QsIGdlbmRlcl9vcmllbnRhdGlvbikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpCgojIE1ldGFkYXRhIG9uIE5FIGNsdXN0ZXIgMwpQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6ZmlsdGVyKFNhbXBsZV9OYW1lICVpbiUgQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlQHBoeWxvJHRpcC5sYWJlbCkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KEdlb19Db3VudHJ5LCBpcy5Ob3J0aEVhc3QsIGdlbmRlcl9vcmllbnRhdGlvbikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpCgojIENvdW50cnkgaW5mbyBvbiBORSBjbHVzdGVyIDMKVFBBLm1ldGEyLjEgJT4lIAogIGRwbHlyOjpmaWx0ZXIoU2FtcGxlX05hbWUgJWluJSBCZWFzdC50cmVlLk5FLmNsdXN0ZXIzLnN1YnRyZWVAcGh5bG8kdGlwLmxhYmVsKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoR2VvX0NvdW50cnkpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKQoKIyBTZXBhcmF0ZSBtZXRhZGF0YSByZWNvcmRzIHNob3cgSHVuZ2FyaWFuIHNhbXBsZSAiVFBBX0hVTjE4MDAwMSIgY2FtZSBmcm9tIGEgbWFsZSBiaXNleHVhbCAoTVNXTSkuCmBgYApcCkV4YW1pbmUgU05QIHNjYWxlZCB0cmVlIGZvciBkaXN0YW5jZXMKYGBge3J9CgojIEV4dHJhY3QgaW5mb3JtYXRpb24gYWJvdXQgU05QIGRpc3RhbmNlcwpUUEEuTkVjbHVzdGVyMy5weWphcnRyZWUubXJjYSA8LSBnZXRNUkNBKFRQQS5weWphci50cmVlLCBhcy5jaGFyYWN0ZXIodW5saXN0KFRQQS5tZXRhMi4xW1RQQS5tZXRhMi4xJFNhbXBsZV9OYW1lICVpbiUgQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlQHBoeWxvJHRpcC5sYWJlbCwiU2FtcGxlX05hbWUiXSkpKQoKClRQQS5ORWNsdXN0ZXIzLnB5amFydHJlZS5zdWJ0cmVlIDwtIHRyZWVfc3Vic2V0KFRQQS5weWphci50cmVlLCBub2RlPVRQQS5ORWNsdXN0ZXIzLnB5amFydHJlZS5tcmNhLCBsZXZlbHNfYmFjaz0xKQoKZ2d0cmVlKFRQQS5ORWNsdXN0ZXIzLnB5amFydHJlZS5zdWJ0cmVlKSArIGdlb21fdGlwbGFiKHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikKZ2d0cmVlKFRQQS5ORWNsdXN0ZXIzLnB5amFydHJlZS5zdWJ0cmVlKSRkYXRhCmBgYAoKXApcCkRvIHNvbWUgYW5hbHlzaXMgb2YgbmVhcmVzdCBuZWlnaGJvdXIgYW5kIGRpc3RhbmNlcyB0byBNUkNBcwpgYGB7cn0KY2FsY3VsYXRlLnllYXJzLmZyb20ubXJjYSA8LSBmdW5jdGlvbihjdXJyZW50LmdndHJlZS5waHlsbywgY3VycmVudC5nZ3RyZWUuZGF0YSl7CiAgI2N1cnJlbnQuZ2d0cmVlIDwtIEJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZQogIGFsbC50aXBzIDwtIGN1cnJlbnQuZ2d0cmVlLnBoeWxvJHRpcC5sYWJlbAogIGRpc3QuMi5tcmNhIDwtIE5VTEwKICAjIyMgcHV0IGRhdGVzIGludG8gZGYKICBjdXJyZW50LmdndHJlZS5kYXRhJG1yY2EubWVkaWFuIDwtIDIwMTkuNSAtIGN1cnJlbnQuZ2d0cmVlLmRhdGEkaGVpZ2h0X21lZGlhbgogIGN1cnJlbnQuZ2d0cmVlLmRhdGEkeWVhciA8LSBhcy5udW1lcmljKHJvdW5kKDIwMTkuNSAtIGN1cnJlbnQuZ2d0cmVlLmRhdGEkaGVpZ2h0X21lZGlhbiwzKSkKICBjdXJyZW50LmdndHJlZS5kYXRhJG1yY2EuOTVoaWdoIDwtIHJvdW5kKDIwMTkuNSAtIHNhcHBseSgxOm5yb3coY3VycmVudC5nZ3RyZWUuZGF0YSksZnVuY3Rpb24oeCkgYXMubnVtZXJpYyh1bmxpc3QoY3VycmVudC5nZ3RyZWUuZGF0YVt4LCJoZWlnaHRfMC45NV9IUEQiXSkpWzFdKSwgMykKICBjdXJyZW50LmdndHJlZS5kYXRhJG1yY2EuOTVsb3cgPC0gcm91bmQoMjAxOS41IC0gc2FwcGx5KDE6bnJvdyhjdXJyZW50LmdndHJlZS5kYXRhKSxmdW5jdGlvbih4KSBhcy5udW1lcmljKHVubGlzdChjdXJyZW50LmdndHJlZS5kYXRhW3gsImhlaWdodF8wLjk1X0hQRCJdKSlbMl0pLCAzKQogICMgZXh0cmFjdCBkYXRlcyBiZXR3ZWVuIHNhbXBsZSBhbmQgaXRzIE1SQ0EgdXNpbmcgbG9vcAogIGZvciAoY3VycmVudC5ub2RlIGluIGFsbC50aXBzKSB7CiAgICBjdXJyZW50LnBhcmVudCA8LSBjKG1hdGNoKGN1cnJlbnQubm9kZSxjdXJyZW50LmdndHJlZS5waHlsbyR0aXAubGFiZWwpLCBwaGFuZ29ybjo6QW5jZXN0b3JzKGN1cnJlbnQuZ2d0cmVlLnBoeWxvLCBtYXRjaChjKGN1cnJlbnQubm9kZSksIGN1cnJlbnQuZ2d0cmVlLnBoeWxvJHRpcC5sYWJlbCksICJwYXJlbnQiKSkKICAgIAogICAgY3VycmVudC5ub2RlbGlzdCA8LSBjdXJyZW50LmdndHJlZS5kYXRhW2N1cnJlbnQuZ2d0cmVlLmRhdGEkbm9kZSAlaW4lIGN1cnJlbnQucGFyZW50LF0KICAgIGN1cnJlbnQuZGlzdC4yLm1yY2EgPC0gYyhjdXJyZW50Lm5vZGUsIGFzLm51bWVyaWMoY3VycmVudC5ub2RlbGlzdFsxLCJ5ZWFyIl0tY3VycmVudC5ub2RlbGlzdFsyLCJ5ZWFyIl0pKQogICAgZGlzdC4yLm1yY2EgPC0gcmJpbmQoZGlzdC4yLm1yY2EsIGN1cnJlbnQuZGlzdC4yLm1yY2EpCiAgfQogIGRpc3QuMi5tcmNhIDwtIGRhdGEuZnJhbWUoU2FtcGxlX05hbWU9YXMuY2hhcmFjdGVyKGRpc3QuMi5tcmNhWywxXSksIGRpc3QudG8ubXJjYT1hcy5udW1lcmljKGRpc3QuMi5tcmNhWywyXSksIHN0cmluZ3NBc0ZhY3RvcnM9RikKICByZXR1cm4oZGlzdC4yLm1yY2EpCn0KCiMjIyBBbGwgc2FtcGxlcyBpbiBnbG9iYWwgdHJlZQpkaXN0Lm1yY2EuYWxsLlRQQSA8LSBjYWxjdWxhdGUueWVhcnMuZnJvbS5tcmNhKGZ1bGwuYmVhc3QyLnRyZWVAcGh5bG8sIGZ1bGwuYmVhc3QyLnRyZWVAZGF0YSkKCmBgYApcCk1lcmdlIGRpc3QyTVJDQSB3aXRoIG1ldGFkYXRhCmBgYHtyfQpQSEUubWV0YWRhdGEubGlua2VkLmRpc3QybXJjYSA8LSBsZWZ0X2pvaW4oUEhFLm1ldGFkYXRhLmxpbmtlZCwgZGlzdC5tcmNhLmFsbC5UUEEsIGJ5PSJTYW1wbGVfTmFtZSIpCgpwLnRpbWUybXJjYS5vcmllbnRhdGlvbiA8LSBnZ3Bsb3QoUEhFLm1ldGFkYXRhLmxpbmtlZC5kaXN0Mm1yY2EsIGFlcyhnZW5kZXJfb3JpZW50YXRpb24sIGRpc3QudG8ubXJjYSwgY29sb3I9Z2VuZGVyX29yaWVudGF0aW9uKSkgKyAKICBnZW9tX3F1YXNpcmFuZG9tKHNpemU9MC43NSwgYWxwaGE9MC41KSArCiAgdGhlbWVfbGlnaHQoKSArIHRoZW1lLnRleHQuc2l6ZSArCiAgY29vcmRfZmxpcCgpICsKICBsYWJzKHg9IkdlbmRlciBPcmllbnRhdGlvbiIsIHk9IlllYXJzIHRvIE1SQ0EiLCBjb2xvcj0iR2VuZGVyIE9yaWVudGF0aW9uIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJywgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iR2VuZGVyXG5PcmllbnRhdGlvbiIsIHZhbHVlcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbi5jb2xzLCBicmVha3M9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24pCgpwLnRpbWUybXJjYS5waGVfcmVnaW9uIDwtIGdncGxvdChQSEUubWV0YWRhdGEubGlua2VkLmRpc3QybXJjYSwgYWVzKHBoZV9jZW50cmUsIGRpc3QudG8ubXJjYSwgY29sb3I9cGhlX2NlbnRyZSkpICsgCiAgZ2VvbV9xdWFzaXJhbmRvbShzaXplPTAuNzUsIGFscGhhPTAuNSkgKwogIHRoZW1lX2xpZ2h0KCkgKyB0aGVtZS50ZXh0LnNpemUgKwogIGNvb3JkX2ZsaXAoeWxpbT1jKDAsNDApKSArCiAgbGFicyh4PSJVS0hTQSBSZWdpb24iLCB5PSJZZWFycyB0byBNUkNBIiwgY29sb3I9IlVLSFNBIFJlZ2lvbiIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249J2JvdHRvbScsIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IlVLSFNBXG5SZWdpb24iLCB2YWx1ZXM9UEhFLnJlZ2lvbi5jb2xzLmJyZXckcmVnaW9uLmNvbCwgYnJlYWtzPVBIRS5yZWdpb24uY29scy5icmV3JFVLSFNBLnJlZ2lvbikKCnAudGltZTJtcmNhLnBoZV9yZWdpb24ub3JpZW50YXRpb24gPC0gZ2dwbG90KFBIRS5tZXRhZGF0YS5saW5rZWQuZGlzdDJtcmNhLCBhZXMocGhlX2NlbnRyZSwgZGlzdC50by5tcmNhLCBjb2xvcj1nZW5kZXJfb3JpZW50YXRpb24pKSArIAogIGdlb21fcXVhc2lyYW5kb20oc2l6ZT0wLjc1LCBhbHBoYT0wLjUpICsKICB0aGVtZV9saWdodCgpICsgdGhlbWUudGV4dC5zaXplICsKICBjb29yZF9mbGlwKHlsaW09YygwLDIwKSkgKwogIGxhYnMoeD0iVUtIU0EgUmVnaW9uIiwgeT0iWWVhcnMgdG8gTVJDQSIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249J2JvdHRvbScsIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IkdlbmRlclxuT3JpZW50YXRpb24iLCB2YWx1ZXM9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24uY29scywgYnJlYWtzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uKQpwLnRpbWUybXJjYS5waGVfcmVnaW9uLm9yaWVudGF0aW9uCgoKcC50aW1lMm1yY2Euc3VibGluZWFnZSA8LSBnZ3Bsb3QoUEhFLm1ldGFkYXRhLmxpbmtlZC5kaXN0Mm1yY2EsIGFlcyhUUEEucGluZWNvbmUuc3VibGluZWFnZSwgZGlzdC50by5tcmNhLCBjb2xvcj1UUEEucGluZWNvbmUuc3VibGluZWFnZSkpICsgCiAgZ2VvbV9xdWFzaXJhbmRvbShzaXplPTAuNzUsIGFscGhhPTAuNSkgKwogIHRoZW1lX2xpZ2h0KCkgKyB0aGVtZS50ZXh0LnNpemUgKwogIGNvb3JkX2ZsaXAoKSArCiAgbGFicyh4PSJUUEEgTGluZWFnZSIsIHk9IlllYXJzIHRvIE1SQ0EiLCBjb2xvcj0iVFBBIExpbmVhZ2UiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSdib3R0b20nLCBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UuY29scywgYnJlYWtzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlKQpwLnRpbWUybXJjYS5zdWJsaW5lYWdlCgoKcC50aW1lMm1yY2EuTGluZWFnZSA8LSBnZ3Bsb3QoUEhFLm1ldGFkYXRhLmxpbmtlZC5kaXN0Mm1yY2EsIGFlcyhUUEFfTGluZWFnZSwgZGlzdC50by5tcmNhLCBjb2xvcj1UUEFfTGluZWFnZSkpICsgCiAgZ2VvbV9xdWFzaXJhbmRvbShzaXplPTAuNzUsIGFscGhhPTAuNSkgKwogIHRoZW1lX2xpZ2h0KCkgKyB0aGVtZS50ZXh0LnNpemUgKwogIGNvb3JkX2ZsaXAoKSArCiAgbGFicyh4PSJUUEEgTGluZWFnZSIsIHk9IlllYXJzIHRvIE1SQ0EgKE1lZGlhbiBvZiBQb3N0ZXJpb3IpIiwgY29sb3I9IlRQQSBMaW5lYWdlIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJywgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPVRQQV9MaW5lYWdlLmNvbHMkTGluZWFnZS5jb2wsIGJyZWFrcz1UUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UpCmBgYApcClwKTWF5YmUgY2FuIG1ha2UgYW4gTVNUIG9mIHRoZSBOb3J0aCBFYXN0IHNhbXBsZXMgZm9yIGdyYXBldHJlZT8KYGBge3J9ClRQQS5weWphci50cmVlLnN1YnNldC5Ob3J0aEVhc3QgPC0gYXBlOjprZWVwLnRpcChUUEEucHlqYXIudHJlZSwgYXMuY2hhcmFjdGVyKHVubGlzdChQSEUubWV0YWRhdGEubGlua2VkW1BIRS5tZXRhZGF0YS5saW5rZWQkcGhlX2NlbnRyZT09Ik5vcnRoIEVhc3QiLCJTYW1wbGVfTmFtZSJdKSkpCgojZ2d0cmVlKFRQQS5weWphci50cmVlLnN1YnNldC5Ob3J0aEVhc3QpCiN3cml0ZS50cmVlKFRQQS5weWphci50cmVlLnN1YnNldC5Ob3J0aEVhc3QsIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiVFBBLlVLLW9ubHktTm9ydGhFYXN0LnB5amFyLjIwMjItMDItMjYudHJlIikpCgojIFdyaXRlIG91dCBhIG1ldGFkYXRhIHNoZWV0IGZvciB0aGUgcmVsZXZhbnQgaW5mb3JtYXRpb24KUEhFLm1ldGFkYXRhLmxpbmtlZC5ncmFwZXRyZWUgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZFssYygiU2FtcGxlX05hbWUiLCAieWVhciIsImdlbmRlcl9vcmllbnRhdGlvbiIsInBoZV9jZW50cmUiLCJoaXZwb3MiLCJ1a2Jvcm4iLCJUUEFfTGluZWFnZSIsIlRQQS5waW5lY29uZS5zdWJsaW5lYWdlIildCmNvbG5hbWVzKFBIRS5tZXRhZGF0YS5saW5rZWQuZ3JhcGV0cmVlKVsxXSA8LSAiSUQiCgojd3JpdGUudGFibGUoUEhFLm1ldGFkYXRhLmxpbmtlZC5ncmFwZXRyZWUsIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiVFBBLlVLLW9ubHkuZ3JhcGV0cmVlLm1ldGEuMjAyMi0wMi0wMy50c3YiKSwgc2VwID0gIlx0IiwgcXVvdGU9Riwgcm93Lm5hbWVzID0gRikKYGBgCgoKQWx0ZXJuYXRpdmUgYXBwcm9hY2ggdXNpbmcgTVNUIGluc3RlYWQgb2YgbmV0d29ya3MgZm9yIE5vcnRoIEVhc3QgZGF0YQpgYGB7cn0KIyBSZWFkIGluIE1TVAojVFBBLk5vcnRoRWFzdEVuZ2xhbmQuR3JhcGV0cmVlLmZpbGUgPC0gcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEtVUstTm9ydGhFYXN0LTIwMjItMDItMjYuR2VuZGVyT3JpZW50YXRpb24tTVNUcmVlLmlua3NjYXBlZC4rbm9kZS1jb3VudHMrR0JNU00uc3ZnIikKCnAuVFBBLk5vcnRoRWFzdEVuZ2xhbmQuR3JhcGV0cmVlIDwtIGdnZHJhdygpICsgZHJhd19pbWFnZShUUEEuTm9ydGhFYXN0RW5nbGFuZC5HcmFwZXRyZWUuZmlsZSkKcC5UUEEuTm9ydGhFYXN0RW5nbGFuZC5HcmFwZXRyZWUKCnAuVFBBLk5vcnRoRWFzdEVuZ2xhbmQuR3JhcGV0cmVlLmhlYWRlciA8LSBwbG90X2dyaWQocC5UUEEuTm9ydGhFYXN0RW5nbGFuZC5HcmFwZXRyZWUsIGxhYmVscz1jKCJBIC0gTmV0d29yayBDbHVzdGVycyAoTm9ydGggRWFzdCBFbmdsYW5kKSIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplLCBzY2FsZT0wLjk1KQoKYGBgClwKUGxvdCB3aXRoIGJlYXN0IHRyZWVzCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTJ9CiNwLlBIRS5Ob3J0aEVhc3RfTVNULndpdGguYmVhc3Quc3VidHJlZXMuY29tYmkgPC0gcGxvdF9ncmlkKHAuVFBBLk5vcnRoRWFzdEVuZ2xhbmQuR3JhcGV0cmVlLCBwLkJlYXN0LnRyZWUuTkUuc3VidHJlZXMuY29tYmkzLCBuY29sPTEsIHJlbF9oZWlnaHRzPWMoMyw2KSwgbGFiZWxzPWMoIkEgLSBOZXR3b3JrIENsdXN0ZXJzIChOb3J0aCBFYXN0IEVuZ2xhbmQpIiwgIiIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplLCBzY2FsZSA9IDAuOTUpCgpwLlBIRS5Ob3J0aEVhc3RfTVNULndpdGguYmVhc3Quc3VidHJlZXMuY29tYmkgPC0gcGxvdF9ncmlkKHAuVFBBLk5vcnRoRWFzdEVuZ2xhbmQuR3JhcGV0cmVlLmhlYWRlciwgcC5CZWFzdC50cmVlLk5FLnN1YnRyZWVzLmNvbWJpMywgbmNvbD0xLCByZWxfaGVpZ2h0cz1jKDMsNykpCgoKCnAuUEhFLk5vcnRoRWFzdF9NU1Qud2l0aC5iZWFzdC5zdWJ0cmVlcy5jb21iaQojZ2dzYXZlKHBhc3RlMChGaWd1cmVfb3V0cHV0X2RpcmVjdG9yeSwiRmlnM19TdWJsaW4xLk5vcnRoRWFzdC5NU1QrQmVhc3QuIixmb3JtYXQoU3lzLkRhdGUoKSwiJVklbSVkIiksIi5wZGYiKSwgdW5pdHM9J21tJywgd2lkdGg9MjAwLCBoZWlnaHQ9MjQ1LCBkZXZpY2U9J3BkZicsIGRwaT0xMjAwKQoKI2dnc2F2ZShwbG90PXAuUEhFLk5vcnRoRWFzdF9NU1Qud2l0aC5iZWFzdC5zdWJ0cmVlcy5jb21iaSwgcGFzdGUwKEZpZ3VyZV9vdXRwdXRfZGlyZWN0b3J5LCJGaWczX1N1YmxpbjEuTm9ydGhFYXN0Lk1TVCtCZWFzdC4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnN2ZyIpLCB1bml0cz0nbW0nLCB3aWR0aD0yMDAsIGhlaWdodD0yNDUsIGRldmljZT1zdmdsaXRlLCBkcGk9MTIwMCkKCgpgYGAKClwKRG8gc29tZSBhbmFseXNpcyBvZiBtYWpvciBzdWJsaW5lYWdlcyBvdmVyIHRpbWUgYnkgcmVnaW9uIC0gY291bGQgdGhpcyBpbmZsdWVuY2Ugb2JzZXJ2YXRpb25zIGFib3V0IHN1YmxpbmVhZ2VzPwpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9NH0KIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGJ5IFBIRSBSZWdpb24KUEhFLm1ham9yLnN1YmxpbmVhZ2UuUEhFY2VudHJlLmRhdGUgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6OmZpbHRlcihUUEEucGluZWNvbmUuc3VibGluZWFnZSAlaW4lIGMoMSwxNCkpICU+JQogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgcGhlX2NlbnRyZSwgeWVhcikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuc3VibGluPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MocGhlX2NlbnRyZSksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnN1YmxpbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKQoKCmdncGxvdChQSEUubWFqb3Iuc3VibGluZWFnZS5QSEVjZW50cmUuZGF0ZSwgYWVzKHllYXIsIHBoZV9jZW50cmUsIHNpemU9Q291bnQsIGNvbG9yPVRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSkgKwogIGdlb21fcG9pbnQoKSArIAogIGZhY2V0X2dyaWQoLn5UUEEucGluZWNvbmUuc3VibGluZWFnZSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZS5jb2xzLCBicmVha3M9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UpCgoKcC5QSEUubWFqb3Iuc3VibGluZWFnZS5QSEVjZW50cmUuZGF0ZS5idWJibGVwbG90IDwtIGdncGxvdChQSEUubWFqb3Iuc3VibGluZWFnZS5QSEVjZW50cmUuZGF0ZSwgYWVzKHllYXIsIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBjb2xvcj1UUEEucGluZWNvbmUuc3VibGluZWFnZSkpICsKICBnZW9tX3BvaW50KGFscGhhPTAuNjUsIGFlcyhzaXplPUNvdW50KSkgKyAKICBnZW9tX2xpbmUoYWxwaGE9MC4yNSkgKwogIGZhY2V0X2dyaWQoZmFjdG9yKGdzdWIoIlxcICIsIlxuIixwaGVfY2VudHJlKSwgbGV2ZWxzPWdzdWIoIlxcICIsIlxuIixQSEUucmVnaW9uLmNvbHMuYnJldyRVS0hTQS5yZWdpb24pKX4uLCBzd2l0Y2g9J3knKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUoc3RyaXAucGxhY2VtZW50ID0gIm91dHNpZGUiKSArCiAgdGhlbWUoc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvcj0nd2hpdGUnLCBmaWxsPSd3aGl0ZScsbGluZXR5cGU9InNvbGlkIiksIHN0cmlwLnRleHQueT1lbGVtZW50X3RleHQoY29sb3IgPSAiZ3JleTI1IixhbmdsZT0wLCBzaXplPTUpKSArIAogIHNjYWxlX3NpemVfYXJlYShtYXhfc2l6ZSA9IDQuNSxicmVha3M9YygxLDUsMTAsMjAsMzAsNDApKSArCiAgdGhlbWUudGV4dC5zaXplICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLmNvbHMsIGJyZWFrcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSkgKyAKICBsYWJzKHk9IlJlZ2lvbiIsIHg9IlllYXIiLCBjb2xvcj0iU3VibGluZWFnZSIpIAogCnAuUEhFLm1ham9yLnN1YmxpbmVhZ2UuUEhFY2VudHJlLmRhdGUuYnViYmxlcGxvdAoKYGBgClwKRG8gc29tZSBzcGVjaWZpYyBhbmFseXNpcyBmb3IgdGhlIDMgTm9ydGhlcm4gcmVnaW9ucwpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9M30KIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGJ5IFBIRSBSZWdpb24KIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpmaWx0ZXIocGhlX2NlbnRyZSAlaW4lIGMoIk5vcnRoIEVhc3QiLCAiTm9ydGggV2VzdCIsICJZb3Jrc2hpcmUgYW5kIEh1bWJlciIpKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50PW4oKSkKCiBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6ZmlsdGVyKHBoZV9jZW50cmUgJWluJSBjKCJOb3J0aCBFYXN0IiwgIk5vcnRoIFdlc3QiLCAiWW9ya3NoaXJlIGFuZCBIdW1iZXIiKSkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KHllYXIpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQ9bigpKQoKCnAuUEhFLm1ham9yLnN1YmxpbmVhZ2UuM05vcnRoZXJuUmVnaW9ucyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6ZmlsdGVyKHBoZV9jZW50cmUgJWluJSBjKCJOb3J0aCBFYXN0IiwgIk5vcnRoIFdlc3QiLCAiWW9ya3NoaXJlIGFuZCBIdW1iZXIiKSkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCB5ZWFyLCBwaGVfY2VudHJlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZ2dwbG90KGFlcyh5ZWFyLCBDb3VudCwgZmlsbD1waGVfY2VudHJlKSkgKyAKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsIHdpZHRoPTAuNjUpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9UEhFLnJlZ2lvbi5jb2xzLmJyZXckcmVnaW9uLmNvbCwgYnJlYWtzPVBIRS5yZWdpb24uY29scy5icmV3JFVLSFNBLnJlZ2lvbikgKwogIHRoZW1lX2J3KCkgKyB0aGVtZS50ZXh0LnNpemUgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDIwMTIsMjAxOCwxKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9cHJldHR5KSArCiAgbGFicyh0aXRsZT0iU2FtcGxlcyBpbiAzIE5vcnRoZXJuIFJlZ2lvbnMiLCB4PSJDb2xsZWN0aW9uIFllYXIiLCB5PSJTYW1wbGUgQ291bnQiLCBmaWxsPSJQdWJsaWMgSGVhbHRoXG5SZWdpb24iKSArCiAgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICAjZ2VvbV90ZXh0KGFlcyh4PXllYXIseT1Db3VudC0wLjUsIGxhYmVsPUNvdW50KSwgY29sb3I9J2dyZXk5NScsIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKwogIE5VTEwKcC5QSEUubWFqb3Iuc3VibGluZWFnZS4zTm9ydGhlcm5SZWdpb25zCgpgYGAKCgoKXApTaW5nbGUgbGlua2FnZSBuZXR3b3JrIG9mIGlkZW50aWNhbCBnZW5vbWVzIGZyb20gVUsKCmBgYHtyfQojIENvbnN0cmFpbiBieSBTTlAgZGlzdGFuY2UgKGlkZW50aWNhbCBpbiB0aGUgYXNyIHNucCB0cmVlKQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2FscyA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGFbUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJERpc3RhbmNlLlBoeWxvPT0wLF0KCiMgYW5kIGEgbWF4IG9mIDIgeWVhcnMKI1BIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzW1BIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9MixdCgoKIyBBbmQgbWFrZSBzdXJlIHRoYXQgd2UgYWN0dWFsbHkgaGF2ZSBnZW5ldGljIGRpc3RhbmNlIGRhdGEgZm9yIGFsbCBzYW1wbGVzIHdpdGhpbiB0aGUgbmV0d29yawpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2FscyA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fsc1shaXMubmEoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMkRGlzdGFuY2UuUGh5bG8pLF0KCiMgcmVtb3ZlIHNlbGYtc2FtcGxlcwpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2FscyA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fsc1tQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2FscyRzYW1lLnNhbXBsZT09ImRpZmZlcmVudCIsXQoKCiMgY2xlYW51cCBzb21lIGRhdGEgbm9pc2UKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHNbIWlzLm5hKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzJHllYXIudDEpLF0KCiMgcHJlcGFyZSBpbnRwdXQgZGF0YSAod2l0aCBlZGdlIGluZm8pClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzLmlucHV0MSA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fsc1ssYygiVGF4YTEiLCJUYXhhMiIsIkRpc3RhbmNlLlBoeWxvIiwiZGVjaW1hbC5kYXRlLmRpc3RhbmNlIiwieWVhci5kaXN0YW5jZSIsIk9yaWVudGF0aW9uLkNsYXNzIiwiZXBpLnRpbWUuZGlzdGFuY2UuY2F0LnllYXJzIiwiZXBpLnRpbWUuZGlzdGFuY2UuY2F0IildCgojIyMjIyMjIyMjIyMKIyBzb21lIGlzc3VlcyB3aXRoIHVwZGF0ZSB0byBSNCAtIGRvdWJsZSBzaWRlZCBtYXRyaXgKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxJGVkZ2VuYW1lIDwtIHNhcHBseSgxOm5yb3coUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxKSwgZnVuY3Rpb24oeCkgcGFzdGUwKHNvcnQoYXMuY2hhcmFjdGVyKHVubGlzdChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDFbeCxjKCJUYXhhMSIsIlRheGEyIildKSkpLGNvbGxhcHNlPSJfX18iKSkKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzLmlucHV0MVshZHVwbGljYXRlZChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDEkZWRnZW5hbWUpLF0KCiMgQWxzbyBoYXZpbmcgYW4gaXNzdWUgd2l0aCB0YXhhIGFzIGZhY3RvcnMgaGVyZQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDEkVGF4YTEgPC0gYXMuY2hhcmFjdGVyKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzLmlucHV0MSRUYXhhMSkKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxJFRheGEyIDwtIGFzLmNoYXJhY3RlcihQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDEkVGF4YTIpCiMjIyMjIyMjIyMjIwojIERlZHVwbGljYXRlCgojaW52ZXJzZSB3ZWlnaHQKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZS5pbnYgPC0gMS8xLyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDEkZGVjaW1hbC5kYXRlLmRpc3RhbmNlKzAuMDQpCgojIE1ha2UgYWN0dWFsIG5ldHdvcmsKc2V0LnNlZWQoMTIzNikKUEhFLmlkZW50aWNhbHMubmV0d29yayA8LSBuZXR3b3JrKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzLmlucHV0MSwgbWF0cml4LnR5cGUgPSAiZWRnZWxpc3QiLCBpZ25vcmUuZXZhbCA9IEZBTFNFLCBkaXJlY3RlZCA9IEYsIGxvb3BzID0gRikKCiNQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnIDwtIGdnbmV0d29yayhQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLCBsYXlvdXQgPSAia2FtYWRha2F3YWkiLCB3ZWlnaHRzID0gImRlY2ltYWwuZGF0ZS5kaXN0YW5jZS5pbnYiKQojUEhFLmlkZW50aWNhbHMubmV0d29yay5nZyA8LSBnZ25ldHdvcmsoUEhFLmlkZW50aWNhbHMubmV0d29yaywgbGF5b3V0ID0gImZydWNodGVybWFucmVpbmdvbGQiLCB3ZWlnaHRzID0gImRlY2ltYWwuZGF0ZS5kaXN0YW5jZSIpClBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cgPC0gZ2duZXR3b3JrKFBIRS5pZGVudGljYWxzLm5ldHdvcmssIGxheW91dCA9ICJmcnVjaHRlcm1hbnJlaW5nb2xkIikKClBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2ckVGF4YTEgPC0gUEhFLmlkZW50aWNhbHMubmV0d29yay5nZyR2ZXJ0ZXgubmFtZXMKCiMgZXh0cmFjdCB0ZW1wb3JhbCBjbHVzdGVycyBmcm9tIG5ldHdvcmsKUEhFLmlkZW50aWNhbHMubmV0d29yay5pZyA8LSBhc0lncmFwaChQSEUuaWRlbnRpY2Fscy5uZXR3b3JrKQpQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmNvbXBvbmVudHMgPC0gZGF0YS5mcmFtZShUYXhhMT1uZXR3b3JrLnZlcnRleC5uYW1lcyhQSEUuaWRlbnRpY2Fscy5uZXR3b3JrKSwgdmVydGV4Lm5vPWFzLnZlY3RvcihWKFBIRS5pZGVudGljYWxzLm5ldHdvcmsuaWcpKSwgY2x1c3Rlcj1pZ3JhcGg6OmNvbXBvbmVudHMoUEhFLmlkZW50aWNhbHMubmV0d29yay5pZykkbWVtYmVyc2hpcCkKUEhFLmlkZW50aWNhbHMubmV0d29yay5jb21wb25lbnRzJENsdXN0ZXIgPC0gcGFzdGUwKCJDbHVzdGVyIixQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmNvbXBvbmVudHMkY2x1c3RlcikKCiMgbWVyZ2UgbWV0YWRhdGEgYmFjayBpbgpQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnIDwtIHBseXI6OmpvaW4oUEhFLmlkZW50aWNhbHMubmV0d29yay5nZywgZGF0YS5mcmFtZShUYXhhMT1QSEUubWV0YWRhdGEubGlua2VkJFNhbXBsZV9OYW1lLCBQSEUubWV0YWRhdGEubGlua2VkWyxjKCJwaGVfY2VudHJlIiwibG9uZG9uIiwieWVhciIsImFnZV9ncm91cCIsInVrYm9ybiIsImdlbmRlcl9vcmllbnRhdGlvbiIsImhpdnBvcyIsIlRQQS5waW5lY29uZS5zdWJsaW5lYWdlIiwiVFBBX0xpbmVhZ2UiKV0sIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSxieT0iVGF4YTEiLCB0eXBlPSJsZWZ0IikKClBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cgPC0gcGx5cjo6am9pbihQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnLCBkYXRhLmZyYW1lKFRheGExPVBIRS5pZGVudGljYWxzLm5ldHdvcmsuY29tcG9uZW50cyRUYXhhMSwgQ2x1c3Rlcj1QSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmNvbXBvbmVudHMkQ2x1c3RlciksIGJ5PSJUYXhhMSIsIHR5cGU9ImxlZnQiKQoKCiMgCiMgQWRkIHRlbXBvcmFsIGNvbG91ciBzY2FsZQojdW5pcXVlKFBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2ckZXBpLnRpbWUuZGlzdGFuY2UuY2F0KQoKZXBpLnRpbWUuZGlzdGFuY2UuY2F0LmNvbHMgPC0gcmV2KGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbCg4LCAiR3JleXMiKSkobGVuZ3RoKHVuaXF1ZShQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnJGVwaS50aW1lLmRpc3RhbmNlLmNhdCkpLTEpKQoKCiMgUGxvdCBuZXR3b3JrCnAuUEhFLmlkZW50aWNhbHMubmV0d29yay4wU05QIDwtIGdncGxvdChQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnLCBhZXMoeCA9IHgsIHkgPSB5LCB4ZW5kID0geGVuZCwgeWVuZCA9IHllbmQpKSArIAogIGdlb21fZWRnZXMoYWxwaGE9MC45MCwgY3VydmF0dXJlID0gMC4yLCBhZXMoY29sb3I9ZmFjdG9yKGVwaS50aW1lLmRpc3RhbmNlLmNhdCksIGxpbmV0eXBlPWZhY3RvcihlcGkudGltZS5kaXN0YW5jZS5jYXQpKSkgKwogICNzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImdyZXk1IiwiZ3JleTM1IiwiZ3JleTU1IiwgImdyZXk2NSIsICJncmV5NzUiKSwgbmFtZT0iU05QXG5EaXN0YW5jZSIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iVGVtcG9yYWxcbkRpc3RhbmNlIiwgdmFsdWVzID0gZXBpLnRpbWUuZGlzdGFuY2UuY2F0LmNvbHMpICsKICBzY2FsZV9saW5ldHlwZShuYW1lPSJUZW1wb3JhbFxuRGlzdGFuY2UiKSArCiAgdGhlbWVfYmxhbmsoKSArCiAgZ2duZXdzY2FsZTo6bmV3X3NjYWxlX2NvbG9yKCkgKyBnZ25ld3NjYWxlOjpuZXdfc2NhbGUoInNpemUiKSArCiAgI2dlb21fbm9kZWxhYmVsKGFlcyhjb2xvcj1nZW5kZXJfb3JpZW50YXRpb24sIGxhYmVsPXBhc3RlKFRheGExLHllYXIsc2VwPSJcbiIpLGZvbnRmYWNlID0gImJvbGQiKSwgYWxwaGE9MC44LCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4tMC40LCBsYWJlbC5zaXplPTAuMTUsIGxhYmVsLnBhZGRpbmcgPSB1bml0KDAuMDUsICJsaW5lcyIpKSArCiAgZ2VvbV9ub2RlcyhzaXplPTIuNSwgYWVzKGNvbG9yPWdlbmRlcl9vcmllbnRhdGlvbiksIGFscGhhPTAuOSkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iR2VuZGVyXG5PcmllbnRhdGlvbiIsIHZhbHVlcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbi5jb2xzLCBicmVha3M9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24pICsgCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICBOVUxMCnAuUEhFLmlkZW50aWNhbHMubmV0d29yay4wU05QCgpgYGAKClBsb3QgdGhpcyBhZ2FpbnN0IGEgVUsgdHJlZT8KYGBge3J9CmdoZWF0bWFwKGdndHJlZShUUEEucHlqYXIudHJlZS5zdWJzZXQudWspLApkYXRhLmZyYW1lKHJvdy5uYW1lcz1QSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmNvbXBvbmVudHMkVGF4YTEsIENsdXN0ZXI9UEhFLmlkZW50aWNhbHMubmV0d29yay5jb21wb25lbnRzJENsdXN0ZXIpKQoKYGBgCgoKClwKU29tZSBzdGF0cyBmcm9tIHRoaXMKYGBge3J9CnAuUEhFLmlkZW50aWNhbC5PcmllbnRhdGlvbl9jbGFzcy5ieWRhdGVkaXN0IDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSAlPiUKICBkcGx5cjo6ZmlsdGVyKHNhbWUuc2FtcGxlPT0iZGlmZmVyZW50IiwgRGlzdGFuY2UuUGh5bG89PTApICU+JQogICNmaWx0ZXIoZGVjaW1hbC5kYXRlLmRpc3RhbmNlPD0xKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoZXBpLnRpbWUuZGlzdGFuY2UuY2F0LCBPcmllbnRhdGlvbi5DbGFzcykgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQuY2xhc3MuZGF0ZT1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUoc3VtLmNsYXNzPXN1bShDb3VudC5jbGFzcy5kYXRlKSwgZnJhY3QuY2xhc3M9Q291bnQuY2xhc3MuZGF0ZS9zdW0uY2xhc3MpICU+JQogIGdncGxvdChhZXMoeD1lcGkudGltZS5kaXN0YW5jZS5jYXQsIHk9Q291bnQuY2xhc3MuZGF0ZSwgZmlsbD1PcmllbnRhdGlvbi5DbGFzcykpICsKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsIHBvc2l0aW9uPSdzdGFjaycpICsKICB0aGVtZV9idygpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogIGxhYnMoeD0iVGltZSBiZXR3ZWVuIHNhbXBsZXMiLCB5PSJJbnRlcmFjdGlvbiBDb3VudCIsIGZpbGw9Ik9yaWVudGF0aW9uIFR5cGUiKQpwLlBIRS5pZGVudGljYWwuT3JpZW50YXRpb25fY2xhc3MuYnlkYXRlZGlzdAoKCiAgCnAuUEhFLmlkZW50aWNhbC5PcmllbnRhdGlvbl9jbGFzcy5ieVplcm9kaXN0LmNsdXN0ZXIgPC0gUEhFLmlkZW50aWNhbHMubmV0d29yay5nZyAlPiUKICBkcGx5cjo6ZmlsdGVyKCFpcy5uYShPcmllbnRhdGlvbi5DbGFzcykpICU+JQogIGRwbHlyOjpncm91cF9ieShDbHVzdGVyLCBPcmllbnRhdGlvbi5DbGFzcykgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQuY2xhc3MuY2x1c3Rlcj1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUoc3VtLmNsYXNzPXN1bShDb3VudC5jbGFzcy5jbHVzdGVyKSwgZnJhY3QuY2xhc3M9Q291bnQuY2xhc3MuY2x1c3Rlci9zdW0uY2xhc3MpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2Moc3VtLmNsYXNzKSkgJT4lCiAgZHBseXI6OnVuZ3JvdXAoKSAlPiUKICBkcGx5cjo6bXV0YXRlKENsdXN0ZXI9YXNfZmFjdG9yKENsdXN0ZXIpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9Q2x1c3RlciwgeT1Db3VudC5jbGFzcy5jbHVzdGVyLCBmaWxsPU9yaWVudGF0aW9uLkNsYXNzKSkgKwogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JywgcG9zaXRpb249J3N0YWNrJykgKyAKICB0aGVtZV9idygpICsKICB4LnRoZW1lLmF4aXMucm90YXRlICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogIGxhYnMoeD0iSWRlbnRpY2FsIEdlbm9tZSBDbHVzdGVyIiwgeT0iSW50ZXJhY3Rpb24gQ291bnQiLCBmaWxsPSJPcmllbnRhdGlvbiBUeXBlIikKcC5QSEUuaWRlbnRpY2FsLk9yaWVudGF0aW9uX2NsYXNzLmJ5WmVyb2Rpc3QuY2x1c3RlcgoKZC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlciA8LSBsZWZ0X2pvaW4oUEhFLmlkZW50aWNhbHMubmV0d29yay5jb21wb25lbnRzWyxjKCJUYXhhMSIsIkNsdXN0ZXIiKV0sIFBIRS5tZXRhZGF0YS5saW5rZWRbLGMoIlNhbXBsZV9OYW1lIiwicGhlX2NlbnRyZSIsImxvbmRvbiIsInllYXIiLCJhZ2VfZ3JvdXAiLCJ1a2Jvcm4iLCJnZW5kZXJfb3JpZW50YXRpb24iLCJoaXZwb3MiLCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIsIlRQQV9MaW5lYWdlIildLCBieT1jKCJUYXhhMSI9IlNhbXBsZV9OYW1lIikpICU+JQogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgQ2x1c3RlciwgZ2VuZGVyX29yaWVudGF0aW9uKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50Lm9yaWVudC5jbHVzdGVyPW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZShjb3VudC5jbHVzdGVyPXN1bShjb3VudC5vcmllbnQuY2x1c3RlciksIGZyYWN0PWNvdW50Lm9yaWVudC5jbHVzdGVyL2NvdW50LmNsdXN0ZXIpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhjb3VudC5jbHVzdGVyKSkgJT4lCiAgZHBseXI6Om11dGF0ZShDbHVzdGVyLm89YXNfZmFjdG9yKENsdXN0ZXIpKQoKCiMgUGxvdCBzYW1wbGUgY291bnRzIGJ5IGdlbm9tZSBjbHVzdGVyIChjb2xvdXJlZCBieSBvcmllbnRhdGlvbikKcC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlciA8LSBkLlBIRS5pZGVudGljYWwuR2VuZGVyT3JpZW50YXRpb24uYnlaZXJvZGlzdC5jbHVzdGVyICU+JQogIGdncGxvdChhZXMoQ2x1c3Rlci5vLCBjb3VudC5vcmllbnQuY2x1c3RlciwgZmlsbD1nZW5kZXJfb3JpZW50YXRpb24pKSArIAogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5Iiwgd2lkdGg9MC42NSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkdlbmRlclxuT3JpZW50YXRpb24iLCB2YWx1ZXM9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24uY29scywgYnJlYWtzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEpKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgeC50aGVtZS5heGlzLnJvdGF0ZSArIAogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDAsNDUsNSkpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogIGxhYnMoeD0iSWRlbnRpY2FsIEdlbm9tZSBDbHVzdGVyIiwgeT0iU2FtcGxlIENvdW50IiwgZmlsbD0iUGF0aWVudCBHZW5kZXIgT3JpZW50YXRpb24iKSAKCiMgQWRkIGRldGFpbHMgb2Ygc3VibGluZWFnZSAgCnAuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgPC0gcC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlciArIAogIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9jb2xvcigpICsKICBnZW9tX3BvaW50KGRhdGE9KGQuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgJT4lIHNlbGVjdChDbHVzdGVyLm8sIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSAlPiUgZGlzdGluY3QoKSksIGFlcyhDbHVzdGVyLm8sIC0xLjUsIGNvbG9yPVRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSwgaW5oZXJpdC5hZXMgPSBGKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UuY29scywgYnJlYWtzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLCBuYW1lPSJTdWJsaW5lYWdlIiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAyKSkgKwogIE5VTEwKCiMgQWRkIGEgc3VibGluZWFnZSBheGlzIGxhYmVsIChiaXQgb2YgYSBoYWNrKQpwLlBIRS5pZGVudGljYWwuR2VuZGVyT3JpZW50YXRpb24uYnlaZXJvZGlzdC5jbHVzdGVyIDwtIHAuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgKyAKICBnZW9tX3RleHQoZGF0YT1kYXRhLmZyYW1lKGxhYj0iU3VibGluZWFnZSIsIHk9LTEuNSwgeD0yOCwgc3RyaW5nc0FzRmFjdG9ycz1GKSwgYWVzKGxhYmVsPWxhYiwgeD14LCB5PXkpLCBoanVzdCA9IDAuMSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpICsKICBjb29yZF9jYXJ0ZXNpYW4oeD1jKDEsIDI3KSwgY2xpcD0nb2ZmJykKICAKcC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlcgoKIyMjIyMjZ3h4eHhnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksIlN1cEZpZzZfSWRlbnRpY2FsLVNOUC1jbHVzdF9vcmllbnRhdGlvbi4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xMjAsIGhlaWdodD0xMDAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCgpgYGAKXApQb3NzaWJsZSB0byBpbnRyb2R1Y2Ugc29tZSBtb3JlIGluZm8gaW50byB0aGF0IHBsb3Q/CgoKYGBge3J9CmQuUEhFLmlkZW50aWNhbC5yZWdpb24uYnlaZXJvZGlzdC5jbHVzdGVyIDwtIGxlZnRfam9pbihQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmNvbXBvbmVudHNbLGMoIlRheGExIiwiQ2x1c3RlciIpXSwgUEhFLm1ldGFkYXRhLmxpbmtlZFssYygiU2FtcGxlX05hbWUiLCJwaGVfY2VudHJlIiwibG9uZG9uIiwieWVhciIsImFnZV9ncm91cCIsInVrYm9ybiIsImdlbmRlcl9vcmllbnRhdGlvbiIsImhpdnBvcyIsIlRQQS5waW5lY29uZS5zdWJsaW5lYWdlIiwiVFBBX0xpbmVhZ2UiKV0sIGJ5PWMoIlRheGExIj0iU2FtcGxlX05hbWUiKSkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBDbHVzdGVyLCBwaGVfY2VudHJlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50LnJlZ2lvbi5jbHVzdGVyPW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZShjb3VudC5jbHVzdGVyPXN1bShjb3VudC5yZWdpb24uY2x1c3RlciksIGZyYWN0PWNvdW50LnJlZ2lvbi5jbHVzdGVyL2NvdW50LmNsdXN0ZXIpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhjb3VudC5jbHVzdGVyKSkgJT4lCiAgZHBseXI6Om11dGF0ZShDbHVzdGVyLm89YXNfZmFjdG9yKENsdXN0ZXIpKQoKCnAuUEhFLmlkZW50aWNhbC5SZWdpb24uYnlaZXJvZGlzdC5jbHVzdGVyIDwtIGQuUEhFLmlkZW50aWNhbC5yZWdpb24uYnlaZXJvZGlzdC5jbHVzdGVyICU+JQogIGdncGxvdChhZXMoQ2x1c3Rlci5vLCBjb3VudC5yZWdpb24uY2x1c3RlciwgZmlsbD1waGVfY2VudHJlKSkgKyAKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHdpZHRoPTAuNjUsIHBvc2l0aW9uPSdmaWxsJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlVLSFNBXG5SZWdpb24iLCB2YWx1ZXM9UEhFLnJlZ2lvbi5jb2xzLmJyZXckcmVnaW9uLmNvbCwgYnJlYWtzPVBIRS5yZWdpb24uY29scy5icmV3JFVLSFNBLnJlZ2lvbiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxKSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHgudGhlbWUuYXhpcy5yb3RhdGUgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDQ1LDUpKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobmNvbD0yKSkgKwogIGxhYnMoeD0iSWRlbnRpY2FsIEdlbm9tZSBDbHVzdGVyIiwgeT0iUmVnaW9uIFByb3BvcnRpb24iLCBmaWxsPSJVS0hTQSBSZWdpb24iKSAKCgpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04fQpwLlBIRS5pZGVudGljYWwuYnlaZXJvZGlzdC5jbHVzdGVyLmJhcmNvbWJpIDwtIHBsb3RfZ3JpZChwLlBIRS5pZGVudGljYWwuR2VuZGVyT3JpZW50YXRpb24uYnlaZXJvZGlzdC5jbHVzdGVyICsgeC50aGVtZS5zdHJpcCwgcC5QSEUuaWRlbnRpY2FsLlJlZ2lvbi5ieVplcm9kaXN0LmNsdXN0ZXIsIG5jb2w9MSwgYXhpcz0icmx0IiwgYWxpZ249VCwgcmVsX2hlaWdodHMgPSBjKDIsMSksIGxhYmVscz1jKCJCIiwiQyIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplKQoKI3AuUEhFLmlkZW50aWNhbC5ieVplcm9kaXN0LmNsdXN0ZXIuYmFyY29tYmkKI3AuUEhFLmlkZW50aWNhbHMubmV0d29yay4wU05QCgpwbG90X2dyaWQocC5QSEUuaWRlbnRpY2Fscy5uZXR3b3JrLjBTTlAsIHAuUEhFLmlkZW50aWNhbC5ieVplcm9kaXN0LmNsdXN0ZXIuYmFyY29tYmksIG5jb2w9MSwgcmVsX2hlaWdodHM9YygyLDMpLCBsYWJlbHM9YygiQSIsIiIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplKQoKCnAuUEhFLmlkZW50aWNhbC5ieVplcm9kaXN0LmNsdXN0ZXIuYmFyY29tYmkubm9OZXQgPC0gcGxvdF9ncmlkKHAuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgKyB4LnRoZW1lLnN0cmlwLCBwLlBIRS5pZGVudGljYWwuUmVnaW9uLmJ5WmVyb2Rpc3QuY2x1c3RlciwgbmNvbD0xLCBheGlzPSJybHQiLCBhbGlnbj1ULCByZWxfaGVpZ2h0cyA9IGMoMiwxKSwgbGFiZWxzPWMoIkEiLCJCIiksIGxhYmVsX3NpemU9cGFuZWwubGFiLnNpemUpCnAuUEhFLmlkZW50aWNhbC5ieVplcm9kaXN0LmNsdXN0ZXIuYmFyY29tYmkubm9OZXQgCgoKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksIlN1cEZpZzZfSWRlbnRpY2FsLVNOUC1jbHVzdF9vcmllbnRhdGlvbi4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xMjAsIGhlaWdodD0xMjAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCmBgYAoKCgpgYGB7cn0KUEhFLmlkZW50aWNhbHMubmV0d29yay5nZy5yZWdpb24uc2NhdHRlcnBpZS5ncm91cHMgPC0gUEhFLmlkZW50aWNhbHMubmV0d29yay5nZyAlPiUKICBkcGx5cjo6c2VsZWN0KENsdXN0ZXIsIFRheGExLCBwaGVfY2VudHJlKSAlPiUKICBkcGx5cjo6ZGlzdGluY3QoKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoQ2x1c3RlciwgcGhlX2NlbnRyZSkgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQuY2VudHJlPW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh4PUNsdXN0ZXIsIHk9My41KSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tPSJwaGVfY2VudHJlIiwgdmFsdWVzX2Zyb209IkNvdW50LmNlbnRyZSIsIHZhbHVlc19maWxsPTApICU+JQogIGRwbHlyOjpzZWxlY3QoQ2x1c3Rlcix4LHksdW5pcXVlKFBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2ckcGhlX2NlbnRyZSkpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgZHBseXI6Om11dGF0ZShDbHVzdGVyLm51bWVyaWM9YXMubnVtZXJpYygxOjI3KSkKICAKCnAuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgKyAKICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpICMrCiAgCgpgYGAKCgoKXApHZXQgYSBmZXcgbW9yZSBzdGF0cyBvbiB0aGUgbGFyZ2VzdCBjbHVzdGVyIChDbHVzdGVyIDgpCmBgYHtyfQojZC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlciAlPiUgZmlsdGVyKENsdXN0ZXI9PSJDbHVzdGVyOCIpCgpQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnLmlkZW50aWNhbC5jbHVzdGVyOCA8LSBQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnICU+JSBmaWx0ZXIoQ2x1c3Rlcj09IkNsdXN0ZXI4IikgICU+JQogIHNlbGVjdCh2ZXJ0ZXgubmFtZXMsIE9yaWVudGF0aW9uLkNsYXNzLCBwaGVfY2VudHJlLCB5ZWFyLCBUUEFfTGluZWFnZSwgVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGhpdnBvcywgQ2x1c3RlcikKCnNvcnQodW5pcXVlKFBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cuaWRlbnRpY2FsLmNsdXN0ZXI4JHllYXIpKQoKYGBgCgpcCkdldCBzb21lIG1vcmUgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGhldGVyb3NleHVhbCBvbmx5IGNsdXN0ZXJzCmBgYHtyfQpQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnLmlkZW50aWNhbF9oZXRlcm9jbHVzdGVycyA8LSBQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnICU+JSBmaWx0ZXIoQ2x1c3RlciAlaW4lIGMoIkNsdXN0ZXIxMiIsICJDbHVzdGVyMjAiLCAiQ2x1c3RlcjI3IikpICAlPiUKICBzZWxlY3QodmVydGV4Lm5hbWVzLCBDbHVzdGVyLCBnZW5kZXJfb3JpZW50YXRpb24sIHBoZV9jZW50cmUsIHllYXIsIFRQQV9MaW5lYWdlLCBUUEEucGluZWNvbmUuc3VibGluZWFnZSwgaGl2cG9zKSAlPiUgCiAgZGlzdGluY3QoKSAlPiUKICBhcnJhbmdlKENsdXN0ZXIsIHllYXIsIGdlbmRlcl9vcmllbnRhdGlvbikKClBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cuaWRlbnRpY2FsX2hldGVyb2NsdXN0ZXJzCmBgYApcIApBbmQgZG8gdGhlIHNhbWUgZm9yIHRoZSBzbWFsbCBtaXhlZC9HQk1TTSBjbHVzdGVycwpgYGB7cn0KUEhFLmlkZW50aWNhbHMubmV0d29yay5nZy5pZGVudGljYWxfbm90LmhldGVyb2NsdXN0ZXJzIDwtIFBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cgJT4lIGZpbHRlcihDbHVzdGVyICVub3RpbiUgYygiQ2x1c3RlcjEyIiwgIkNsdXN0ZXIyMCIsICJDbHVzdGVyMjciLCAiQ2x1c3RlcjgiKSkgICU+JQogIHNlbGVjdCh2ZXJ0ZXgubmFtZXMsIENsdXN0ZXIsIGdlbmRlcl9vcmllbnRhdGlvbiwgcGhlX2NlbnRyZSwgeWVhciwgVFBBX0xpbmVhZ2UsIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBoaXZwb3MpICU+JSAKICBkaXN0aW5jdCgpICU+JQogIGFycmFuZ2UoQ2x1c3RlciwgeWVhciwgZ2VuZGVyX29yaWVudGF0aW9uKQpQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnLmlkZW50aWNhbF9ub3QuaGV0ZXJvY2x1c3RlcnMKYGBgCgoKCldoYXQgcHJvcG9ydGlvbiBvZiBoZXRlcm9zZXh1YWxzIGhhdmUgYW4gaWRlbnRpY2FsIEdCTVNNIHBhaXJlZCBnZW5vbWU/ClwKYGBge3J9CgojIERlbGluZWF0ZSBoZXRlcm9zZXh1YWwgY2x1c3RlcnMKZC5QSEUuaWRlbnRpY2FsLmhldGVyb3NleHVhbC5jbHVzdGVycyA8LSBkLlBIRS5pZGVudGljYWwuR2VuZGVyT3JpZW50YXRpb24uYnlaZXJvZGlzdC5jbHVzdGVyICU+JSAKICBkcGx5cjo6bXV0YXRlKGlzLmhldGVyb3NleHVhbD1pZmVsc2UoZ2VuZGVyX29yaWVudGF0aW9uJWluJSBjKCJNU1ciLCAiV1NNIiksICJoZXRlcm9zZXh1YWwiLCBpZmVsc2UoZ2VuZGVyX29yaWVudGF0aW9uPT0iR0JNU00iLCJHQk1TTSIsICJVbmtub3duIikpKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoQ2x1c3Rlcixpcy5oZXRlcm9zZXh1YWwpICU+JSAKICBkcGx5cjo6bXV0YXRlKGNvdW50LmhldGVybz1zdW0oY291bnQub3JpZW50LmNsdXN0ZXIpLCBmcmFjdC5oZXRlcm89c3VtKGNvdW50Lm9yaWVudC5jbHVzdGVyKS9jb3VudC5jbHVzdGVyKSAlPiUKICBkcGx5cjo6dW5ncm91cCgpICU+JQogIGRwbHlyOjpmaWx0ZXIoaXMuaGV0ZXJvc2V4dWFsPT0iaGV0ZXJvc2V4dWFsIikgJT4lIAogIGRwbHlyOjpzZWxlY3QoLWMoY291bnQub3JpZW50LmNsdXN0ZXIsIGdlbmRlcl9vcmllbnRhdGlvbiwgZnJhY3QpKSAlPiUKICBkcGx5cjo6ZGlzdGluY3QoKSAlPiUKICBkcGx5cjo6bXV0YXRlKGNsdXN0ZXIudHlwZT1pZmVsc2UoZnJhY3QuaGV0ZXJvPT0xLCAiaGV0ZXJvLm9ubHkiLCAib3RoZXIiKSkKCmQuUEhFLmlkZW50aWNhbC5oZXRlcm9zZXh1YWwuY2x1c3RlcnMgCgojIFdoYXQgcHJvcG9ydGlvbiBvZiBoZXRlcm9zZXh1YWxzIChuPTIwKSBhcmUgaW4gYSBoZXRlcm9zZXh1YWwtb25seSBjbHVzdGVyPwpkLlBIRS5pZGVudGljYWwuaGV0ZXJvc2V4dWFsLmNsdXN0ZXJzICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoY2x1c3Rlci50eXBlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50LmluLmhldGVyby5jbHVzdGVyPXN1bShjb3VudC5oZXRlcm8pKSAlPiUgCiAgZHBseXI6Om11dGF0ZShmcmFjdC5pbi5oZXRlcm89Y291bnQuaW4uaGV0ZXJvLmNsdXN0ZXIvc3VtKGNvdW50LmluLmhldGVyby5jbHVzdGVyKSkKICAKCiNsZWZ0X2pvaW4oUEhFLmlkZW50aWNhbHMubmV0d29yay5jb21wb25lbnRzWyxjKCJUYXhhMSIsIkNsdXN0ZXIiKV0sIFBIRS5tZXRhZGF0YS5saW5rZWRbLGMoIlNhbXBsZV9OYW1lIiwicGhlX2NlbnRyZSIsImxvbmRvbiIsInllYXIiLCJhZ2VfZ3JvdXAiLCJ1a2Jvcm4iLCJnZW5kZXJfb3JpZW50YXRpb24iLCJoaXZwb3MiLCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIsIlRQQV9MaW5lYWdlIildLCBieT1jKCJUYXhhMSI9IlNhbXBsZV9OYW1lIikpCmBgYAoKXAoKCiMgUmV2aXNpb25zIDAzLTIwMjMgb253YXJkcwoKXApMb29rIGF0IHByb3BvcnRpb24gb2YgZ2Vub21lcyBhdCBkaWZmZXJlbnQgY292ZXJhZ2UgdGhyZXNob2xkcwpgYGB7cn0KIyBDdW11bGF0aXZlIHByb3BvcnRpb24gb2YgTiBjb3VudHMgaW4gZ2Vub21lcwpQSEUubWV0YWRhdGEuTmNvdW50LmN1bW11bGF0aXZlLlVLIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpmaWx0ZXIoaXMuVUs9PSJVSyIpICU+JQogIGRwbHlyOjpncm91cF9ieShgUHJvcG9ydGlvbi1OXz41X21hcHBpbmcrbWFza2luZ19OaWNob2xzYCkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuQ291bnQ9c3VtKENvdW50KSkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5Db3VudCwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9jb3VudD1jdW1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6bXV0YXRlKERhdGFzZXQ9IlVLIChuPTIzNykiKQpQSEUubWV0YWRhdGEuTmNvdW50LmN1bW11bGF0aXZlLlVLCgoKUEhFLm1ldGFkYXRhLk5jb3VudC5jdW1tdWxhdGl2ZS5BTEwgPC0gVFBBLm1ldGEyLjEgJT4lIAogIGRwbHlyOjpmaWx0ZXIoZnVsbC50ZW1wb3JhbC5hbmFseXNpcz09IlllcyIpICU+JQogIGRwbHlyOjpncm91cF9ieShgUHJvcG9ydGlvbi1OXz41X21hcHBpbmcrbWFza2luZ19OaWNob2xzYCkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuQ291bnQ9c3VtKENvdW50KSkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5Db3VudCwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9jb3VudD1jdW1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6bXV0YXRlKERhdGFzZXQ9IkFsbCAobj01MjApIikgClBIRS5tZXRhZGF0YS5OY291bnQuY3VtbXVsYXRpdmUuQUxMClBIRS5tZXRhZGF0YS5OY291bnQuY3VtbXVsYXRpdmUuY29tYmkgPC0gcmJpbmQoUEhFLm1ldGFkYXRhLk5jb3VudC5jdW1tdWxhdGl2ZS5VSywgUEhFLm1ldGFkYXRhLk5jb3VudC5jdW1tdWxhdGl2ZS5BTEwpCgoKCgpwLmN1bXVsYXRpdmUuTmNvdW50LmZvci5kYXRzZXQgPC0gIGdncGxvdChQSEUubWV0YWRhdGEuTmNvdW50LmN1bW11bGF0aXZlLmNvbWJpICwgYWVzKGBQcm9wb3J0aW9uLU5fPjVfbWFwcGluZyttYXNraW5nX05pY2hvbHNgLCBjdW1fZnJhY3QsIGdyb3VwPURhdGFzZXQsIGNvbG9yPURhdGFzZXQpKSArIAogIGdlb21fcG9pbnQoYWxwaGE9MC43NSwgc2l6ZT0xKSArCiAgdGhlbWVfbGlnaHQoKSArIAogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICd0b3AnKSArCiAgbGFicyh5PSJDdW11bGF0aXZlIGZyYWN0aW9uIG9mIGdlbm9tZXMiLCB4PSJQcm9wb3J0aW9uIG9mIHNpdGVzIG1hc2tlZCB0byBOIikgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDAsMSwwLjEpKQoKcC5jdW11bGF0aXZlLk5jb3VudC5mb3IuZGF0c2V0CmBgYAoKXApCRUFTVCA5NSUgSFBEIGNhbGN1bGF0aW9ucyAocHJvdmlkZSBtb3JlIGRldGFpbHMgZm9yIDUyMCBkYXRhc2V0ICAgICkKYGBge3J9CkJFQVNULm1lZGlhbiA8LSAxLjI4ZS03CkJFQVNULjk1SFBEIDwtIGMoMS4wN2UtNywgMS40OGUtNykKU1MxNC5hbG4ubGVuZ3RoIDwtIDExMzk1NjkKCgoxLyhCRUFTVC5tZWRpYW4gKiBTUzE0LmFsbi5sZW5ndGgpCjEvKEJFQVNULjk1SFBEICogU1MxNC5hbG4ubGVuZ3RoKQpgYGAKClwKXApGdXJ0aGVyIGV2YWx1YXRpb24gb2Ygc3VibGluZWFnZSA2IChyZXZpZXdlciByZXNwb25zZSkgdXNpbmcgYW5jZXN0cmFsIHJlY29uc3RydWN0aW9uIHBlcmZvcm1lZCBvbiB0aGUgZ2xvYmFsIFRQQS1vbmx5IGFsaWdubWVudC90cmVlIHVzZWQgaW4gQmVhbGUgMjAyMS4KCmBgYHtyfQpUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnRyZWUgPC0gcmVhZC5uZXh1cyhUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnRyZWUuZmlsZSkKVFBBLnRyZWV0aW1lLmFuY2VzdHJhbC50cmVlLmRhdGEgPC0gZm9ydGlmeShUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnRyZWUpCgpnZ3RyZWUoVFBBLnRyZWV0aW1lLmFuY2VzdHJhbC50cmVlKSArIGdlb21fbm9kZWxhYihzaXplPTIpCgojIFJlYWQgaW4gYW5kIHByb2Nlc3MgVFBBLW9ubHkgdmNmICh0byBjb25maXJtIHNpdGVzIGFyZSB0aGUgc2FtZSkKVFBBLm9ubHkubWlkcG9pbnQudHJlZXRpbWUuYW5jZXN0cmFsLnZjZiA8LSByZWFkLnZjZlIoVFBBLnRyZWV0aW1lLmFuY2VzdHJhbC52Y2YuZmlsZSwgdmVyYm9zZSA9IEZBTFNFKQpUUEEub25seS5taWRwb2ludC50cmVldGltZS5hbmNlc3RyYWwudmNmLmZpeCA8LSBnZXRGSVgoVFBBLm9ubHkubWlkcG9pbnQudHJlZXRpbWUuYW5jZXN0cmFsLnZjZikKVFBBLm9ubHkubWlkcG9pbnQudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5maXggPC0gZGF0YS5mcmFtZShUUEEub25seS5taWRwb2ludC50cmVldGltZS5hbmNlc3RyYWwudmNmLmZpeFssYygyLDQsNSldLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKVFBBLm9ubHkubWlkcG9pbnQudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5maXgkaW4uVFBBLm9ubHkgPC0gInllcyIKVFBBLm9ubHkubWlkcG9pbnQudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5maXgkS2V5IDwtIDE6bnJvdyhUUEEub25seS5taWRwb2ludC50cmVldGltZS5hbmNlc3RyYWwudmNmLmZpeCkKCmBgYAoKXApFeHRyYWN0IGdlbm90eXBlIHNpdGVzCmBgYHtyfQpUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5ndCA8LSBleHRyYWN0X2d0X3RpZHkoVFBBLm9ubHkubWlkcG9pbnQudHJlZXRpbWUuYW5jZXN0cmFsLnZjZikKClRQQS50cmVldGltZS5hbmNlc3RyYWwudmNmLmd0LmYgPC0gcGx5cjo6am9pbihUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5ndCwgVFBBLm9ubHkubWlkcG9pbnQudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5maXhbLGMoIktleSIsIlBPUyIpXSwgYnk9IktleSIsIHR5cGU9ImxlZnQiKQoKVFBBLnRyZWV0aW1lLmFuY2VzdHJhbC52Y2YuZ3QuZiRQT1MgPC0gYXMubnVtZXJpYyhUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5ndC5mJFBPUykKVFBBLnRyZWV0aW1lLmFuY2VzdHJhbC52Y2YuZ3QuZiRndF9HVCA8LSBhcy5udW1lcmljKFRQQS50cmVldGltZS5hbmNlc3RyYWwudmNmLmd0LmYkZ3RfR1QpCgpUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5ndC5mLnNwcmVhZCA8LSB0aWR5cjo6c3ByZWFkKFRQQS50cmVldGltZS5hbmNlc3RyYWwudmNmLmd0LmZbLGMoIlBPUyIsIkluZGl2IiwiZ3RfR1QiKV0sIFBPUywgZ3RfR1QpIAoKYGBgCgpVc2Ugc25wRWZmIHRvIGFubm90YXRlIG11bHRpLXZjZiwgYW5kIHRoZW4gcHVsbCBpbiBhbm5vdGF0aW9ucyBoZXJlCmBgYHtyfQpUUEEuc25wRWZmIDwtIHJlYWQudGFibGUoVFBBLnNucEVmZi5maWxlLGhlYWRlciA9IFQsIGNoZWNrLm5hbWVzID0gRiwgY29tbWVudC5jaGFyID0gIiIsc2VwPSJcdCIpCgpUUEEuc25wRWZmLmZpbHQgPC0gVFBBLnNucEVmZlshKFRQQS5zbnBFZmYkYEFOTlsqXS5HRU5FYD09ImdlbmUtVFBBU1NfUlMwMDA0MCIgJiBUUEEuc25wRWZmJGBBTk5bKl0uRUZGRUNUYD09ImludHJhZ2VuaWNfdmFyaWFudCIpLF0KVFBBLnNucEVmZi5maWx0W1RQQS5zbnBFZmYuZmlsdCRgQU5OWypdLkVGRkVDVGA9PSIuIiwiQU5OWypdLkVGRkVDVCJdIDwtICJpbnRyYWdlbmljX3ZhcmlhbnQiCgoKVFBBLnNucEVmZi5maWx0ICU+JSBkcGx5cjo6Z3JvdXBfYnkoYEFOTlsqXS5FRkZFQ1RgKSAlPiUgc3VtbWFyaXNlKENvdW50PW4oKSkKVFBBLnNucEVmZi5maWx0ICU+JSBkcGx5cjo6Z3JvdXBfYnkoYEFOTlsqXS5HRU5FYCkgJT4lIHN1bW1hcmlzZShDb3VudD1uKCkpClRQQS5zbnBFZmYuZmlsdCAlPiUgZHBseXI6Omdyb3VwX2J5KGBBTk5bKl0uR0VORWAsYEFOTlsqXS5FRkZFQ1RgKSAlPiUgc3VtbWFyaXNlKENvdW50PW4oKSkKClRQQS5zbnBFZmYuZmlsdC52YXIucGVyLnBvcyA8LSBUUEEuc25wRWZmLmZpbHQgJT4lIGRwbHlyOjpncm91cF9ieShQT1MpICU+JSBzdW1tYXJpc2UoQ291bnQ9bigpKQpUUEEuc25wRWZmLmZpbHQudmFyLnBlci5wb3MubXVsdGkgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIodW5saXN0KFRQQS5zbnBFZmYuZmlsdC52YXIucGVyLnBvc1tUUEEuc25wRWZmLmZpbHQudmFyLnBlci5wb3MkQ291bnQ+MSwiUE9TIl0pKSkKClRQQS5zbnBFZmYuZmlsdFtUUEEuc25wRWZmLmZpbHQkUE9TICVpbiUgVFBBLnNucEVmZi5maWx0LnZhci5wZXIucG9zLm11bHRpLF0KCmBgYAoKXApMZXRzIHB1bGwgaW4gZ2VuZSBmdW5jdGlvbiAod2hlcmUga25vd24pIGZvciB0aGVzZSBzaXRlcyBmcm9tIHRoZSBnZmYKYGBge3J9ClNTMTQuZ2ZmIDwtIGFwZTo6cmVhZC5nZmYoU1MxNC5nZmYuZmlsZSkKU1MxNC5nZmYuY2RzIDwtIFNTMTQuZ2ZmW1NTMTQuZ2ZmJHR5cGU9PSJDRFMiLF0KCiMjIyMgZnVuY3Rpb24gdG8gZXh0cmFjdCBkaWZmZXJlbnQgZmllbGRzIGZyb20gYXR0cmlidXRlcyBjb2x1bW4KZ2V0QXR0cmlidXRlRmllbGQgPC0gZnVuY3Rpb24gKHgsIGZpZWxkLCBhdHRyc2VwID0gIjsiKSB7CiAgICAgcyA9IHN0cnNwbGl0KHgsIHNwbGl0ID0gYXR0cnNlcCwgZml4ZWQgPSBUUlVFKQogICAgIHNhcHBseShzLCBmdW5jdGlvbihhdHRzKSB7CiAgICAgICAgIGEgPSBzdHJzcGxpdChhdHRzLCBzcGxpdCA9ICI9IiwgZml4ZWQgPSBUUlVFKQogICAgICAgICBtID0gbWF0Y2goZmllbGQsIHNhcHBseShhLCAiWyIsIDEpKQogICAgICAgICBpZiAoIWlzLm5hKG0pKSB7CiAgICAgICAgICAgICBydiA9IGFbW21dXVsyXQogICAgICAgICB9CiAgICAgICAgIGVsc2UgewogICAgICAgICAgICAgcnYgPSBhcy5jaGFyYWN0ZXIoTkEpCiAgICAgICAgIH0KICAgICAgICAgcmV0dXJuKHJ2KQogICAgIH0pCn0KIyMjCiNnZXRBdHRyaWJ1dGVGaWVsZChTUzE0LmdmZi5jZHMkYXR0cmlidXRlcywgIk5hbWUiKQoKCiMgRXh0cmFjdCBhdHRyaWJ1dGUgZWxlbWVudHMgZnJvbSBnZmYKU1MxNC5nZmYuY2RzJGdlbmVpZCA8LSBnc3ViKCJnZW5lXFwtIiwiIixnZXRBdHRyaWJ1dGVGaWVsZChTUzE0LmdmZi5jZHMkYXR0cmlidXRlcywgIlBhcmVudCIpKQpTUzE0LmdmZi5jZHMkbG9jdXNfdGFnIDwtIGdldEF0dHJpYnV0ZUZpZWxkKFNTMTQuZ2ZmLmNkcyRhdHRyaWJ1dGVzLCAibG9jdXNfdGFnIikKU1MxNC5nZmYuY2RzJGdlbmUgPC0gZ2V0QXR0cmlidXRlRmllbGQoU1MxNC5nZmYuY2RzJGF0dHJpYnV0ZXMsICJnZW5lIikKU1MxNC5nZmYuY2RzJHByb2R1Y3QgPC0gZ2V0QXR0cmlidXRlRmllbGQoU1MxNC5nZmYuY2RzJGF0dHJpYnV0ZXMsICJwcm9kdWN0IikKU1MxNC5nZmYuY2RzJHByb3RlaW5pZCA8LSBnZXRBdHRyaWJ1dGVGaWVsZChTUzE0LmdmZi5jZHMkYXR0cmlidXRlcywgInByb3RlaW5faWQiKQojIGNyZWF0ZSBhIG1lcmdlZCBsb2N1c190YWcvZ2VuZSB0aGUgd2F5IHNucEVmZiBkb2VzClNTMTQuZ2ZmLmNkcyRnZW5laWQgPC0gc2FwcGx5KDE6bnJvdyhTUzE0LmdmZi5jZHMpLCBmdW5jdGlvbih4KSBpZmVsc2UoaXMubmEoU1MxNC5nZmYuY2RzJGdlbmVbeF0pLFNTMTQuZ2ZmLmNkcyRsb2N1c190YWdbeF0sIFNTMTQuZ2ZmLmNkcyRnZW5lW3hdKSkKU1MxNC5nZmYuY2RzJGdlbmUuY29vcmRzIDwtIHBhc3RlMChTUzE0LmdmZi5jZHMkc3RhcnQsIjoiLFNTMTQuZ2ZmLmNkcyRlbmQpCgpTUzE0LmdmZi5jZHMKYGBgCgpcCiMgcmVhZCBpbiBzbnAgY2xhc3NpZmljYXRpb25zLCBhbmQgYXBwbHkgdG8gZGlzY3JpbWluYXRvcnkgU05QcwpcCldyaXRlIHRoaXMgYXMgYSBmdW5jdGlvbi4gVGFrZXMgNCBhcmd1bWVudHM6Ci0gZGF0YWZyYW1lIG9mIHNucHMgZm9yIGVhY2ggc2FtcGxlIGluIHdpZGUgbWF0cml4IGZvcm1hdCAoZS5nLiBUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5ndC5mLnNwcmVhZCkKLSBsb25nZm9ybSBsaXN0IG9mIFNOUHMgYW5kIHBvc3NpYmxlIGFsbGVsZXMgKGUuZy4gVFBBLnRyZWV0aW1lLmFuY2VzdHJhbC52Y2YuZml4KQotIHZhcmlhbnQgYW5ub3RhdGlvbnMgZGF0YWZyYW1lIChlLmcuIFRQQS5zbnBFZmYuZmlsdCkKLSBhIHZlY3RvciBvZiB0d28gbm9kZXMgaW4gdGhlIHRyZWUgdG8gY29tcGFyZSAoZS5nLiB0dC5ub2Rlcy50by5jb21wYXJlLlNTMTQpClwKYGBge3J9CmV4dHJhY3RfYnJhbmNoX3NpdGVfYWxsZWxpY19mdW5jdGlvbnMgPC0gZnVuY3Rpb24oYWxsZWxlLm1hdHJpeC5zcHJlYWQsIHNucC50YWJsZSwgc25wLmFubm90YXRpb24udGFibGUsIG5vZGVzLmxpc3QpewogICMgZmlsdGVyIFNOUCBtYXRyaXggdG8gb25seSBpbmNsdWRlIHRoZSB0d28gbm9kZXMgb2YgaW50ZXJlc3QKICBkaXNjcmltaW5hdG9yeS5zaXRlczEgPC0gYWxsZWxlLm1hdHJpeC5zcHJlYWRbYWxsZWxlLm1hdHJpeC5zcHJlYWQkSW5kaXYgJWluJSBub2Rlcy5saXN0LF0KICBkaXNjcmltaW5hdG9yeS5zaXRlczIgPC0gdGlkeXI6OmdhdGhlcihkaXNjcmltaW5hdG9yeS5zaXRlczEsUE9TLEd0LC1JbmRpdikgJT4lIAogIHRpZHlyOjpzcHJlYWQoSW5kaXYsIEd0KQogICMgRmlsdGVyIFNOUHMgdW5kZXIgY29uc2lkZXJhdGlvbiB0byB0aG9zZSB0aGF0IGFyZSBkaWZmZXJlbnQgYmV0d2VlbiB0aGUgdHdvIG5vZGVzCiAgZGlzY3JpbWluYXRvcnkuc2l0ZXMyIDwtIGRpc2NyaW1pbmF0b3J5LnNpdGVzMlsoZGlzY3JpbWluYXRvcnkuc2l0ZXMyWywyXSE9ZGlzY3JpbWluYXRvcnkuc2l0ZXMyWywzXSksXQogIGRpc2NyaW1pbmF0b3J5LnNpdGVzMiA8LSBkaXNjcmltaW5hdG9yeS5zaXRlczJbb3JkZXIoYXMubnVtZXJpYyhkaXNjcmltaW5hdG9yeS5zaXRlczIkUE9TKSksXQogICMgbWVyZ2UgaW4gdGhlIGRldGFpbHMgYWJvdXQgYWxsZWxlcyBhdCBlYWNoIHJlbGV2YW50IFNOUCBwb3NpdGlvbgogIGRpc2NyaW1pbmF0b3J5LnNpdGVzMiA8LSBwbHlyOjpqb2luKGRpc2NyaW1pbmF0b3J5LnNpdGVzMiwgc25wLnRhYmxlLGJ5PWMoJ1BPUycpLCB0eXBlPSdsZWZ0JykKICAjIGRlYWwgd2l0aCBtdWx0aS1hbGxlbGljIHNpdGVzLCBhbmQgZGlzY3JpbWluYXRlIGJldHdlZW4gdGhlbQogIGRpc2NyaW1pbmF0b3J5LnNpdGVzMiRBTFQubXVsdGkgPC0gZGlzY3JpbWluYXRvcnkuc2l0ZXMyJEFMVAogIGRpc2NyaW1pbmF0b3J5LnNpdGVzMiRBTFQgPC0gc2FwcGx5KDE6bnJvdyhkaXNjcmltaW5hdG9yeS5zaXRlczIpLCBmdW5jdGlvbih4KSBzdHJzcGxpdChkaXNjcmltaW5hdG9yeS5zaXRlczIkQUxULm11bHRpW3hdLCIsIilbWzFdXVtzb3J0KGFzLm51bWVyaWMoKChkaXNjcmltaW5hdG9yeS5zaXRlczJbeCxjKDIsMyldKSkpKVsyXV0pCiAgIyBtZXJnZSBpbiB0aGUgYW5ub3RhdGlvbiBmb3IgdGhlIGFwcHJvcHJpYXRlIGFsbGVsZS9TTlBzCiAgZGlzY3JpbWluYXRvcnkuc2l0ZXMyLnNucGVmZiA8LSBwbHlyOjpqb2luKHNucC5hbm5vdGF0aW9uLnRhYmxlWyxjKCJQT1MiLCJBTFQiLCJBTk5bKl0uQUxMRUxFIiwiQU5OWypdLkVGRkVDVCIsIkFOTlsqXS5HRU5FIiwiQU5OWypdLkhHVlNfQyIsIkFOTlsqXS5IR1ZTX1AiKV0sIGRpc2NyaW1pbmF0b3J5LnNpdGVzMlssYygiUE9TIiwiUkVGIiwiQUxUIixub2Rlcy5saXN0KV0sIHR5cGU9InJpZ2h0IiwgYnk9YygiUE9TIiwiQUxUIikpCiAgZGlzY3JpbWluYXRvcnkuc2l0ZXMyLnNucGVmZltpcy5uYShkaXNjcmltaW5hdG9yeS5zaXRlczIuc25wZWZmJGBBTk5bKl0uRUZGRUNUYCksIkFOTlsqXS5FRkZFQ1QiXSA8LSAiaW50cmFnZW5pY192YXJpYW50IgogICMgcmV0dXJuIG91dHB1dAogIHJldHVybihkaXNjcmltaW5hdG9yeS5zaXRlczIuc25wZWZmKQp9CmBgYAoKXApgYGB7cn0KI3R0Lm5vZGVzLnRvLmNvbXBhcmUuU1MxNC52cy5OaWNob2xzLlRQQSA8LSBjKCJOT0RFXzAwMDAwMDUiLCJOT0RFXzAwMDAxMDMiKQoKI3R0Lm5vZGVzLnRvLmNvbXBhcmUuc3VibGluZWFnZTYudnMuTVJDQS5UUEEgPC0gYygiTk9ERV8wMDAwMDAzIiwiTk9ERV8wMDAwMDAyIikKdHQubm9kZXMudG8uY29tcGFyZS5zdWJsaW5lYWdlNi52cy5NUkNBLlRQQSA8LSBjKCJOT0RFXzAwMDAwMDEiLCJOT0RFXzAwMDAwMDIiKQoKc3VibGluNi52cy5tcmNhLk5pY2hvbHMuYnJhbmNoX3NpdGVfYWxsZWxlcy5UUEEgPC0gZXh0cmFjdF9icmFuY2hfc2l0ZV9hbGxlbGljX2Z1bmN0aW9ucyhUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5ndC5mLnNwcmVhZCxUUEEub25seS5taWRwb2ludC50cmVldGltZS5hbmNlc3RyYWwudmNmLmZpeCxUUEEuc25wRWZmLmZpbHQsIHR0Lm5vZGVzLnRvLmNvbXBhcmUuc3VibGluZWFnZTYudnMuTVJDQS5UUEEpCgpzdWJsaW42LnZzLm1yY2EuTmljaG9scy5icmFuY2hfc2l0ZV9hbGxlbGVzLlRQQSAlPiUgZHBseXI6Omdyb3VwX2J5KGBBTk5bKl0uRUZGRUNUYCkgJT4lIGRwbHlyOjpzdW1tYXJpc2UoY291bnQ9bigpKQoKcGFzdGUwKCJBbGwgVmFyaWFudHM6ICIsIG5yb3coc3VibGluNi52cy5tcmNhLk5pY2hvbHMuYnJhbmNoX3NpdGVfYWxsZWxlcy5UUEEpKQpwYXN0ZTAoIlVuaXF1ZSBTaXRlczogIiwgbGVuZ3RoKHVuaXF1ZShzdWJsaW42LnZzLm1yY2EuTmljaG9scy5icmFuY2hfc2l0ZV9hbGxlbGVzLlRQQSRQT1MpKSkKcGFzdGUwKCJTeW5vbnltb3VzIFZhcmlhbnRzOiAiLCBucm93KHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBW3N1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBJGBBTk5bKl0uRUZGRUNUYD09InN5bm9ueW1vdXNfdmFyaWFudCIsXSkpCnBhc3RlMCgiTm9uLVN5bm9ueW1vdXMgVmFyaWFudHM6ICIsIG5yb3coc3VibGluNi52cy5tcmNhLk5pY2hvbHMuYnJhbmNoX3NpdGVfYWxsZWxlcy5UUEFbc3VibGluNi52cy5tcmNhLk5pY2hvbHMuYnJhbmNoX3NpdGVfYWxsZWxlcy5UUEEkYEFOTlsqXS5FRkZFQ1RgPT0ibWlzc2Vuc2VfdmFyaWFudCIsXSkpCnBhc3RlMCgiSW50cmFnZW5pYyBWYXJpYW50cyA6IiwgbnJvdyhzdWJsaW42LnZzLm1yY2EuTmljaG9scy5icmFuY2hfc2l0ZV9hbGxlbGVzLlRQQVtzdWJsaW42LnZzLm1yY2EuTmljaG9scy5icmFuY2hfc2l0ZV9hbGxlbGVzLlRQQSRgQU5OWypdLkVGRkVDVGA9PSJpbnRyYWdlbmljX3ZhcmlhbnQiLF0pKQoKCgpzdWJsaW42LnZzLm1yY2EuTmljaG9scy5icmFuY2hfc2l0ZV9hbGxlbGVzLlRQQSRkaXN0LmZyb20ubGFzdC52YXIgPC0gYygwLCBzYXBwbHkoMjpucm93KHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBKSAsIGZ1bmN0aW9uKHgpIGFzLm51bWVyaWMoc3VibGluNi52cy5tcmNhLk5pY2hvbHMuYnJhbmNoX3NpdGVfYWxsZWxlcy5UUEEkUE9TW3hdKSAtIGFzLm51bWVyaWMoc3VibGluNi52cy5tcmNhLk5pY2hvbHMuYnJhbmNoX3NpdGVfYWxsZWxlcy5UUEEkUE9TW3gtMV0pKSkgCgptZWFuKHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBJGRpc3QuZnJvbS5sYXN0LnZhcikKbWVkaWFuKHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBJGRpc3QuZnJvbS5sYXN0LnZhcikKbWluKHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBJGRpc3QuZnJvbS5sYXN0LnZhcikKbWF4KHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBJGRpc3QuZnJvbS5sYXN0LnZhcikgIAoKcC5zdWJsaW5lYWdlNi5hbmNlc3RyYWwuU05Qcy5nZW5vbWVwb3MgPC0gZ2dwbG90KHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBLCBhZXMoeD1hcy5udW1lcmljKFBPUyksIHk9ZGlzdC5mcm9tLmxhc3QudmFyKSkgKyAKICBnZW9tX3BvaW50KHNpemU9MSwgYWxwaGE9MC41KSArCiAgI2dlb21fYmFyKHN0YXQ9J2lkZW50aXR5JywgYWxwaGE9MC41KSArCiAgI2dlb21fbGluZShhbHBoYT0wLjEpICsKICB0aGVtZV9saWdodCgpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW09YygwLFNTMTQuYWxuLmxlbmd0aCkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXByZXR0eSkgKwogIHNjYWxlX3lfbG9nMTAoKSArCiAgbGFicyh4PSJTUzE0IEdlbm9tZSBQb3NpdGlvbiAoTkNfMDIxNTA4LjE7IChicCkpIiwgeT0iRGlzdGFuY2Ugb2YgdmFyaWFudCBmcm9tIHByZXZpb3VzIHZhcmlhbnQgc2l0ZSAoYnApIiwgdGl0bGU9Ikdlbm9tZSBwb3NpdGlvbiBvZiBTTlBzIGRlbGluZWF0aW5nIFN1YmxpbmVhZ2UgNiBmcm9tIE1SQ0Egbm9kZSIpCiAgCnAuc3VibGluZWFnZTYuYW5jZXN0cmFsLlNOUHMuZ2Vub21lcG9zCgoKCnAuc3VibGluZWFnZTYuYW5jZXN0cmFsLlNOUHMuZGlzdC5iZXR3ZWVuLmhpc3RvIDwtIHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBICU+JQogIGdncGxvdChhZXMoeD1kaXN0LmZyb20ubGFzdC52YXIpKSArIAogIHNjYWxlX3hfbG9nMTAoKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucz01MCkgKyAKICB0aGVtZV9saWdodCgpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgbGFicyh4PSJEaXN0YW5jZSBvZiB2YXJpYW50IGZyb20gcHJldmlvdXMgdmFyaWFudCBzaXRlIChicCkiLCB5PSJDb3VudCIpICsgY29vcmRfZmxpcCgpCgpwLnN1YmxpbmVhZ2U2LmFuY2VzdHJhbC5TTlBzLmRpc3QuYmV0d2Vlbi5oaXN0bwoKcGxvdF9ncmlkKHAuc3VibGluZWFnZTYuYW5jZXN0cmFsLlNOUHMuZ2Vub21lcG9zLCBwLnN1YmxpbmVhZ2U2LmFuY2VzdHJhbC5TTlBzLmRpc3QuYmV0d2Vlbi5oaXN0byArIHkudGhlbWUuc3RyaXAgLCByZWxfd2lkdGhzID0gYyg4LDEpLCBhbGlnbiA9IFQpCmBgYApcClwKRG8gc29tZSBmdXJ0aGVyIGFuYWx5c2lzIG9mIHRoZSBOb3J0aCBFYXN0IHN1YmxpbmVhZ2UgZGlzdHJpYnV0aW9ucy4gV2UgaGF2ZSAzNSBzYW1wbGVzIGNvbGxlY3RlZCBmcm9tIHRoZXNlIHJlZ2lvbnMsIG9mIHdoaWNoIDE3IHdlcmUgY29sbGVjdGVkIGZyb20gMjAxNCBvbndhcmRzLiBJcyBzdWJsaW5lYWdlIDE0IG1pc3NpbmcgYnkgY2hhbmNlIChjb3VsZCB3ZSBiZSBtaXNzaW5nIGl0IHNpbXBseSBiZWNhdXNlIHdlIGhhdmVuJ3QgY29sbGVjdGVkIGVub3VnaCBzYW1wbGVzKSBvciBpcyB0aGlzIG1vcmUgbGlrZWx5IHRvIHJlZmxlY3QgdHJ1ZSB1bmV2ZW4gcmVnaW9uYWwgZGlzdHJpYnV0aW9ucz8KYGBge3J9CiMgSG93IG1hbnkgZ2Vub21lcyBmb3VuZCBpbiBOb3J0aGVybiByZWdpb25zIGJlZm9yZSBhbmQgYWZ0ZXIgZmlyc3QgZGV0ZWN0aW9uIG9mIHN1YmxpbmVhZ2UgMTQgaW4gMjAxND8KIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lCiAgZHBseXI6Om11dGF0ZShiZWZvcmUyMDE0PWlmZWxzZSh5ZWFyPj0yMDE0LCIyMDE0b253YXJkcyIsICJwcmUyMDE0IikpICU+JQogIGRwbHlyOjpmaWx0ZXIocGhlX2NlbnRyZSAlaW4lIGMoIk5vcnRoIEVhc3QiLCAiTm9ydGggV2VzdCIsICJZb3Jrc2hpcmUgYW5kIEh1bWJlciIpKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoYmVmb3JlMjAxNCkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudD1uKCkpCgojIFdoYXQgYXJlIHRoZSBwcm9wb3J0aW9ucyBvZiBkaWZmZXJlbnQgc3VibGluZWFnZXMgYXJvdW5kIHRoZSBVSyBiZWZvcmUgYW5kIGFmdGVyIDIwMTQ/ClBIRS5tZXRhLnBvc3QyMDE0LnN1Ymxpbi5mcmFjcyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICAjZHBseXI6OmZpbHRlcih5ZWFyPj0yMDE0KSAlPiUKICBkcGx5cjo6bXV0YXRlKGJlZm9yZTIwMTQ9aWZlbHNlKHllYXI+PTIwMTQsIjIwMTRvbndhcmRzIiwgInByZTIwMTQiKSkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KGJlZm9yZTIwMTQsIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5hbGw9c3VtKENvdW50KSkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5hbGwpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShjdW1fZnJhY3QgPSBjdW1zdW0oZnJhY3Rpb24pKSAlPiUKICBkcGx5cjo6bXV0YXRlKGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKSAlPiUKICBkcGx5cjo6bXV0YXRlKExpbmVhZ2UucGVyYz0oQ291bnQvc3VtKENvdW50KSoxMDApKQpQSEUubWV0YS5wb3N0MjAxNC5zdWJsaW4uZnJhY3MgCgoKIyBzaW11bGF0aW5nIHBvaXNzb24gcHJvY2VzcyByIHRvIHdvcmsgb3V0IGhvdyBtYW55IHNhbXBsZXMgd2Ugd291bGQgZXhwZWN0IGluIE5vcnRoZXJuIEVuZ2xhbmQgdW5kZXIgcG9pc3NvbiBkaXN0cmlidXRpb24KCiMgV2hhdCAlIG9mIHN1YmxpbmVhZ2UgMTQgc2FtcGxlcyBhcmUgZm91bmQgaW4gdGhlIHRvdGFsIHBvcHVsYXRpb24/CnBvc3QyMDE0LnN1YmxpbjE0LmZyZXEgPC0gUEhFLm1ldGEucG9zdDIwMTQuc3VibGluLmZyYWNzICU+JSBmaWx0ZXIoYmVmb3JlMjAxND09IjIwMTRvbndhcmRzIiwgVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2U9PTE0KSAlPiUgc2VsZWN0KExpbmVhZ2UucGVyYykgJT4lIHB1bGwoKQogCgojIFNpbXVsYXRlIGFuZCBwbG90IGEgUG9pc3NvbiBkaXN0cmlidXRpb24gb2YgaG93IG1hbnkgc3VibGluZWFnZSAxNCBzYW1wbGVzIHdlIHdvdWxkIGV4cGVjdCB0byBmaW5kIGlmIHdlIHJhbmRvbWx5IHNlbGVjdGVkIDE3IHNhbXBsZXMgYXQgMjIlIApkYXRhLmZyYW1lKHJwb2lzPXJwb2lzKDEwMDAwMDAsIDE3LygxMDAvcG9zdDIwMTQuc3VibGluMTQuZnJlcSkpKSAlPiUKICBnZ3Bsb3QoYWVzKHJwb2lzKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aD0xKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwyMCwyKSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIGxhYnMoeD0iU2FtcGxlcyBGb3VuZCIsIHk9IlNpbXVsYXRpb24gQ291bnQiKQoKIyBXaGF0IGFyZSB0aGUgcXVhbnRpbGUgZGlzdHJpYnV0aW9ucyBmcm9tIHRoYXQ/CnF1YW50aWxlKHJwb2lzKDEwMDAwMDAsIDE3LygxMDAvcG9zdDIwMTQuc3VibGluMTQuZnJlcSkpLCBwcm9icz1jKDAuMDEsIDAuMDUsIDAuNSwgMC45NSwgMC45OSkpCm1lZGlhbihycG9pcygxMDAwMDAwLCAxNy8oMTAwL3Bvc3QyMDE0LnN1YmxpbjE0LmZyZXEpKSkKbWVhbihycG9pcygxMDAwMDAwLCAxNy8oMTAwL3Bvc3QyMDE0LnN1YmxpbjE0LmZyZXEpKSkKCiMgV2hhdCBpcyB0aGUgcHJvYmFiaWxpdHkgb2YgZmluZGluZyBubyBzYW1wbGVzIChhc3N1bWluZyB1bmlmb3JtIHVuYmlhc2VkIGNvdmVyYWdlKT8KZGF0YS5mcmFtZShuPXNlcSgwLDIwLDEpLCBkcG9pcz1zYXBwbHkoc2VxKDAsMjAsMSksIGZ1bmN0aW9uKHgpIGRwb2lzKHgsIGxhbWJkYT0xNy8oMTAwL3Bvc3QyMDE0LnN1YmxpbjE0LmZyZXEpKSkpICU+JSAKICBnZ3Bsb3QoYWVzKHg9biwgeT1kcG9pcykpICsgCiAgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1wcmV0dHkpICsKICB0aGVtZV9saWdodCgpICsKICBsYWJzKHg9IlNhbXBsZXMgRm91bmQiLCB5PSJQcm9iYWJpbGl0eSIpCgpwYXN0ZSgiUHJvYmFiaWxpdHkgb2YgZmluZGluZyB6ZXJvIHNhbXBsZXMgaXMgIiwgcm91bmQoZHBvaXMoMCwgbGFtYmRhPTE3LygxMDAvcG9zdDIwMTQuc3VibGluMTQuZnJlcSkpLCA1KSkgCmBgYAoKCgo=